I am fairly new to programming and am working with Objective-C in Xcode 5.
I'm presently making an OSX application in Xcode that uses Cramer's Rule (this matrix math method to calculate the intersecting point of three lines).
I really need some help with this one concept- I need to be able to take the user's input from multiple text boxes (assign them all a variable), put them through cramer's rule, and feed the answer out through a label.
I've made the storyboard and assigned one of the 12 text boxes (to test it) as an outlet and the label as an outlet and a button as an action, and tried a few different ways to just take the user input and (unaltered) feed it back out through the label so I know what I'm working with before I get into the math, and it's been unsuccessful. Having major syntax problems.
I have attached my code below:
//
// NewClass.h
// Cramer's Rule
#import <Foundation/Foundation.h>
#interface NewClass : NSViewController <NSTextFieldDelegate> {
IBOutlet NSTextField *box_a;
IBOutlet NSTextField *coord;
NSString *string;
}
#property (nonatomic, retain) IBOutlet NSTextField *box_a;
#property (nonatomic, retain) IBOutlet NSTextField *coord;
- (IBAction)calculate:(id)sender;
#end
AND
//
// NewClass.m
// Cramer's Rule
#import "NewClass.h"
#implementation NewClass
#synthesize box_a;
#synthesize coord;
- (IBAction)calculate:(id)sender {
NSTextField * input=box_a;
coord =input;
}
#end
As far as I know, I have the most up to date version of Xcode, and there is no option for creating a storyboard for an OSX project. Storyboards are for iOS projects. And that would explain the reason why you're unable to hook any thing up from the storyboard to your code.
This isn't to say that a storyboard can't be put in an OSX project--it can't. But it can't be selected from the Cocoa section of new files to create--only the Cocoa Touch section, which is iOS stuff--not OSX.
You have to use NSTextFieldDelegate, it have callback methods like in iOS:
- (void)textDidBeginEditing:(NSNotification *)notification;
- (void)textDidEndEditing:(NSNotification *)notification;
- (void)textDidChange:(NSNotification *)notification;
- (BOOL)acceptsFirstResponder;
For example:
- (void)textDidChange:(NSNotification *)notification{
if ([notification object]== box_a)
{
// ...
}else if ([notification object]== box_b)
{
// ...
}
}
Your problem is more fundamental than syntactical, you need to go and study up on what various things are and how they behave, this includes: variables, properties, objects and object references.
To briefly introduce why you're going wrong: Think of an object as a building. What is "in" the building may change over time, but the address of the building (usually!) does not. An address refers you to a building, and that is what an object reference does.
A variable is a box which holds a value of some type, that value can change over time, but the box does not.
When you declare:
NSTextField *input;
You are requesting that a variable be created for you which can hold references to objects - it does not hold an object anymore than address is a building, it just tells you where to find an object.
When you then assign a value to your variable:
NSTextField *input = box_a;
You are requesting the the value in box_a be copied and placed (stored) in input. That value is an object reference, it is not an object. Whatever object was referenced by box_a is not altered in anyway by this statement - what is in the house doesn't change, you just write the house's address down somewhere else.
When you then do:
coord = input;
you are doing the same thing - copying addresses. No objects are altered. The objects you are referring to are of type NSTextField, they have a visual representation on the screen, copying their addresses doesn't alter that visual representation anymore than copying the address of a building changes what is in the building.
When it comes to properties your code suggests a confusion between a property, which is a piece of code which does something, and its backing variable, a variable which that piece of code operates on.
Understanding these concepts is vital. You need to go an study up some more on programming.
HTH
Related
Im stuck on this problem and it's driving me crazy. What I am attempting to do is have a button click create an instance of a custom class, set it's variables, add it to an NSMutableArray, and display it in a table view. So far it seems that I have everything working except having the info display in the table view.
My custom class TradePaperback just has three NSString properties: title, volume, and publisher.
Here is the code for my header and implementation files:
Header:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (nonatomic, strong) NSMutableArray *tradeArray;
#property (weak) IBOutlet NSTableView *tableView;
- (IBAction)addTrade:(id)sender;
#end
implementation file:
#import "AppDelegate.h"
#import "tradePaperback.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
-(NSMutableArray *)tradeArray{
if (!_tradeArray){
_tradeArray = [[NSMutableArray alloc]init];
TradePaperback *avengers = [[TradePaperback alloc]init];
avengers.title = #"Avengers";
avengers.volume= #"volume 01";
avengers.publisher = #"Marvel Comics";
[_tradeArray addObject:avengers];}
return _tradeArray;}
- (IBAction)addTrade:(id)sender {
TradePaperback *newTrade = [[TradePaperback alloc]init];
newTrade.title = #"New Trade";
newTrade.volume = #"Volume Number";
newTrade.publisher = #"publisher";
[_tradeArray addObject:newTrade];
NSLog(#"added");
NSLog(#"number of items in array is %ld", _tradeArray.count);
[_tableView reloadData];}
#end
My table view is hooked up using bindings. It seems that everything is hooked up correctly since the avengers instance of TadePaperback that i put in shows up when I run the program. As I click the add button I can see from the log in the console that the array is having items added to it, but they just won't display.
Why would the tableView show the first item in the array, but none of the rest?
Here is a picture of the program after running and clicking the add button a couple of times.
program running
I would greatly appreciate any help or advice you guys could give. Thanks in advance.
-Jack
If you're using bindings to populate a table view, you're almost certainly using an NSArrayController to manage the table's content. If you want to add an object to the array controlled by this controller (your tradeArray object), you should do so indirectly: add to the array controller, which will in turn update your array. NSArrayController provides a number of add... or insert.. methods; decide which one suits you best, and use it to replace your call to [_tradeArray addObject:...].
The reason this approach works, and yours doesn't is because this engages the key-value coding/observing machinery that underpins bindings. Your approach essentially adds the object behind the array controller's back - it's not KVO/KVC-compliant, so the array controller remains unaware of the change. Cocoa does provide you with a way of editing the original array in a KVO/KVC-friendly manner if you so wish, it's just a little more work: try the code below, then read Apple's NSKeyValueCoding Protocol Reference and their Key-Value Coding Programming Guide for an explanation:
// Instead of [_tradeArray addObject:newTrade];
[[self mutableArrayValueForKey:#"_tradeArray"] addObject:newTrade];
I have a very simple toy app with one single text view and one single button.
These are controlled by the AppController class that looks like this
// AppController.h
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject <NSTextViewDelegate>
#property IBOutlet NSTextView *textView;
-(IBAction)apply:(id)sender;
#end
// AppController.m
#import "AppController.h"
#implementation AppController {
NSString *name;
}
-(void)awakeFromNib {
name = #"Bob";
_textView.string = name;
}
-(IBAction)apply:(id)sender {
name = _textView.string;
NSLog(#"%#", name);
}
-(void)textDidChange:(NSNotification *)notification {
NSLog(#"%#", name);
}
#end
When the user enters a new value in the textView and clicks on the apply button, I want the instance variable name to get this new value. And this is what happens the first time I click on the Apply button.
But the next time I change the value in the textView the instance variable gets automatically changed without the user clicking on the Apply Button!
This is an unwanted behavior since I did not make any bindings. And indeed if I change the textView to a textField the strange behavior does not happen.
So it seems that NSTextView does some strange involuntary binding. Can someone explain this strange behavior and how to come around it. I do not want the textView to change my data behind my back.
I have made a video on YouTube that shows the difference in behavior between the textField and the textView which you can se at https://youtu.be/qenGKp_L4qs.
I have checked on both Xcode6 and Xcode5 and the strange behavior happens on both.
If you want to test this out on your own machine, do not forget to connect the delegate property of the NSTextView with the AppController in the IB.
This line:
name = _textView.string;
makes your instance variable refer to the same object that the text view is using internally. From the documentation for the string property of NSText (which NSTextView inherits from):
For performance reasons, this method returns the current backing store of the text object. If you want to maintain a snapshot of this as you manipulate the text storage, you should make a copy of the appropriate substring.
When you say "I do not want the textView to change my data behind my back", you've got it wrong. The text view is changing its data and you are (unwisely) treating that as your own.
You should a) make a (possibly private) property for your internal state, b) use the copy attribute on that property, and c) use self.name = _textView.string; to assign to your internal state.
If you don't want to do that, you have to at least copy the string manually, using name = [_textView.string copy];.
I have a UITabBarController that manages two ViewControllers. The first is a UIViewController that allows the user to change game settings. The second is a GLKViewController that runs the game simulation.
I'm trying to enable the Game ViewController to fetch the settings from the Settings ViewController. I have a Slider on the Settings View that represents "Speed".
I have a reference to the other controller, but I'm unable to expose the variable that backs my Slider properly.
SecondViewController.h
#interface SecondViewController : UIViewController{
IBOutlet UISlider * mySlider;
}
property (nonatomic,retain) IBOutlet UISlider * mySlider;
#end
SecondViewController.m
- (IBAction) mySliderWasMoved:(id)sender;
#implementation SecondViewController
#synthesize mySlider;
- (IBAction) mySliderWasMoved:(id)sender{
};
ThirdViewController.m
NSArray *tmpVCs = self.parentViewController.tabBarController.viewControllers;
UIViewController *tmpVC = [tmpVCs objectAtIndex:1]; //obtain handle to SecondViewController
//NSNumber *mySpeed = tmpVC.mySlider; //doesn't see mySlider
NSNumber *mySpeed = [tmpVC mySlider]; //doesn't see mySlider
I'm new to this, and there are many aspects of my project to learn - so I'm not trying to learn how to manage data at this time. I just need to know how to access an instance variable
As mention on the comments,
Use NSDefault to save the value on slider changed. On the very first time of loading your application, you will want to set a default value.
Use Singleton Object to store value.
We understand that, quoting from you " not trying to learn data persistence at this time. Nor do I need architecture direction.", but the rule of thumb here is that you probably will be able to access the instance variable in some way or the other but i think having the best approach will benefit you greatly.
Just my 2 cent.
Fort the benefit of others: I grabbed a handle to the other class, but I hadn't declared the return type as the correct type of class.
Replace:
UIViewController *tmpVC = [tmpVCs objectAtIndex:1];
With:
SecondViewController *tmpVC = [tmpVCs objectAtIndex:1];
Now I have access to the properties that are specific to the SecondViewController.
basicaly I am a C# developer but started learning Objective-C couple of last days.
Now I have to do an exercise which need to create a class and link instance variables (properties) to the UIControls values of the View (e.g. UITextField string value).
Meaning I have already implemented the desired IBOutlets in the ViewControler and inside this controler I will create an instance of the created class. In C# a class could implement the INotifyPropertyChanged interface, bind the class to the controls and notify the object when the Datasource value has changed.
Is there anything equal to this concept in Objective C? Or how can I achieve something like that, only through events when value changed for every Control?
Thank you.
Your question and grammar is a bit ambiguous but it seems to me what you want is custom (manual) property getters/setters. Try this:
#intrface AClass: NSObject {
int iVar;
}
#property (nonatomic, assign) int iVar;
#end
#implementation AClass
- (int)iVar
{
// notify object of value being read, then:
return iVar;
}
- (void)setIVar:(int)_iVar
{
iVar = _iVar;
// then notify object about property being set
}
#end
Not 100% sure what you're asking for from your question. Are you asking whether the ViewController views can auto-update when your model changes?
There are a bunch of different mechanisms for providing notifications between objects/classes/etc. The main ones are as follows (I've included IBAction which you probably know for completeness):
1) IBAction - For UI controls just as you've connected IBOutlets in your UIViewController class, you can also fire events (touch up/touch down/etc) on user interaction.
2) NSNotification - you can post these pretty much anywhere:
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003701
3) Key-Value Observing:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i
I have a workspace that has two projects in it. The first project was essentially a test and develop project where I got things working before worrying about tying everything together for real. The second project is bringing all my individually developed view controllers together in a storyboard.
On one of the view controllers I have a bunch of swipe gestures with quite a bit of UIView animation calls nicely formatted for readability and therefore taking a lot of space. I elected to move them out as a category.
The problem is that the compiler is not seeing the instance variable declarations in the main header file.
What has me pulling my hair out is that I did this in the first project and it all worked fine. So I'm carefully comparing the contents of my second project to the first and I see no differences.
Here're some file snippets to help demonstrate how/where I'm defining things, and then snippets of code in the category file that is attempting to access them:
GSBViewController.h
#interface GSBViewController : UIViewController
#property (strong, nonatomic) IBOutlet UISegmentedControl *roundPicker;
#property (strong, nonatomic) IBOutlet UIView *roundsSectionView;
GSBViewController.m
#import "GSBViewController+Swipe.h"
#interface GSBGameBuilderViewController ()
{
UIBarButtonItem *rightGatherBarButton;
NSInteger previousRound;
}
#end
#implementation GSBViewController
#synthesize roundPicker;
#synthesize roundsSectionView;
GSBViewController+Swipe.h
#import "GSBViewController.h"
#interface GSBViewController (Swipe)
- (void)establishSwipeGestures;
#end
GSBViewController+Swipe.m
#import "GSBViewController+Swipe.h"
#implementation GSBViewController (Swipe)
- (void)establishSwipeGestures
{
UISwipeGestureRecognizer *swipeLeft =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(roundsSectionLeft:)];
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[swipeLeft setNumberOfTouchesRequired:1];
[roundsSectionView addGestureRecognizer:swipeLeft];
// bunch-o-code snipped -- for the time being it's actually all commented out
// as a test and because the LLVM compiler was giving up after too many errors
// and I wanted to see if there was more it would like to tell me about this first --
// and very representative -- problem.
}
#end
The complaint from the compiler is "Use of undeclared identifier 'roundsSectionView'"
If I option-click on the use of roundsSectionView in that line of code where I'm adding the gesture recognizer to it the pop-up correctly describes it as declared in GSBViewController.h
So I'm stumped.
Is there something I can do in Xcode (4.3.2 at the time of this posting :-) to let me see what the included files are? Or is there something non-file-based that is needed to tie a category into the class it's augmenting? I don't remember anything like that being necessary before. In fact, the way I generated the files for this category was through Xcode's File -> New File... Objective-C Category template. Then I just copied the contents of the old ...+Swipe.h and ...+Swipe.m files and pasted them into their respective files in the new project.
A synthesized ivar is private. The compiler won't allow you to access it anywhere execept in the #implementation block where it's created. Neither categories nor subclasses can access the ivar directly; they must use the property: [self roundsSectionView].
There's a slight possibilty that earlier Clangs didn't make synthesized ivars private. Either that or you weren't really doing exactly the same thing in the earlier project.
What #Jacques Cousteau says is correct.
Since you just defined a property and no backing ivar, the category won't be able to access it. If you use self.roundsSectionView it will use the getter method generated for the property and hence it will work.
Or you could define a backing variable in your interface
#interface GSBViewController : UIViewController
{
UIBarButtonItem *roundsSectionView;
}
In this case the categories will be able to access the variable. But not any other class.