NSStepper Not Changing Both Values - objective-c

Here is my current code for my class...
#interface Stat : NSObject {
#private
IBOutlet NSTextField *value;
IBOutlet NSTextField *modValue;
IBOutlet NSStepper *stepper;
}
-(IBAction)setValue:(id)sender;
#end
#implementation Stat
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
-(IBAction)setValue:(id)sender
{
[value setIntValue:([sender intValue])];
[modValue setIntValue:(round(([sender intValue]/2)-5))];
}
#end
The stepper, value text box, and modValue text box are all linked to their corresponding variables, and the stepper is linked to the setValue function. However, when I use the stepper, only the text in the modValue text changes. Can anyone help? If you need any more code/info I can provide it.
Edit: Also. If you do have the solution, can you please explain a bit? I've coded in Java and C# for a long time now, however Obj-C is giving me a challenge. So far I love it though. :)

Edit: it is most likely that one of your outlets in Interface Builder is not properly set up. Check to make sure that both NSTextFields in IB are connected to the correct outlets in Xcode.

According to the Documentation,
"When the value changes, the stepper sends the UIControlEventValueChanged flag to its target (see addTarget:action:forControlEvents:). Refer to the description of the continuous property for information about whether value change events are sent continuously or when user interaction ends."
So you set the state for your UIStepper IBAction as UIControlEventValueChanged.

Related

Cocoa Subclassing weirdness

I'm trying to understand how Subclassing works in Cocoa.
I've created a new Cocoa Application Project in XCode 5.1.
I drag a new Custom View onto the main window.
I create a new Objective-C class CustomViewClass and set it as a Subclass of NSView. This generates the following :
CustomViewClass.h
#import <Cocoa/Cocoa.h>
#interface CustomViewClass : NSView
#end
CustomViewClass.m
#import "CustomViewClass.h"
#implementation CustomViewClass
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
NSLog(#"Custom View initialised");
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
#end
Note that I added the NSLog(#"Custom View initialised"); line so I can track what is going on.
In interface Builder, I select the Custom View and within the Idenditiy Inspecter set it's custom Class to CustomView. Then I run the Application.
As expected I get a Custom View initialised message in the Console.
I do exactly the same with an NSTextField adding it to the window, creating a new class TextFieldClass and the NSTextField custom Class is to TextFieldClass. I also add a NSLog(#"Text Field initialised"); in the same place as above to track things.
However when I run the App, I only get the Custom View initialised message in the Console and not the NSLog(#"Text Field initialised");message.
So initially I think that NSTextField doesn't recieve the initWithFrame message when it is created. So I add an initialiser to TextFieldClass :
- (id)init {
self = [super init];
if (self) {
// Initialization code here.
NSLog(#"Text Field initialised");
}
return self;
}
However this still doesn't seem to get called.
I assumed therefore that NSTextField just wasn't being subclassed. However, when I add this method to TextFieldClass :
-(void)textDidChange:(NSNotification *)notification {
NSLog(#"My text changed");
}
Run the app and lo and behold, every time I type in the text field I get the My text changed message in the Console.
So my question is, what is going on here? How does the NSTextField get initialized and how can you override it's initialiser?
Why does the Custom View seem to act differently to the NSTextField?
Source code here
For your first question, NSTextFiled gets initialised via
- (id)initWithCoder:(NSCoder *)aDecoder
In this case, you have dragged a NSTextField from the palette and then changed the class to your custom text field class in the identity inspector. Hence the initWithCoder: will be called instead of initWithFrame:. The same is true for any object (other than Custom View) dragged from the palette
Instead, if you drag "Custom View" from the palette and change the class to your custom text field class, the initWithFrame: will be invoked.
The CustomViewClass you have created is the second case, hence initWithFrame: is invoked. The TextFieldClass is the first case, hence initWithCoder: is invoked.
If you use the Interface Builder in XCode, you should use awakeFromNib to initialise your subclass.
- (void)awakeFromNib
{
// Your init code here.
}
If you want to use your subclass programatically and using the interface builder, then use code like this:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initView];
}
return self;
}
- (void)awakeFromNib
{
[self initView];
}
- (void)initView
{
// Your init code here
}

How to properly use delegation for NSTextField

Im trying to implement a delegate for a NSTextField object so I can detect the user input in real time and give some feedback about no allowed input in that particular field.
Especially, I want to simulate the onChange() method from JavaScript, detecting the user input in real time and show him a warning if it is writing a non supported value.
i.e. The app have a text field it only accept numeric values from 0 to 255 (like RGB values) and I want to know when the user is writing not numeric values or out of range values to instantly show him a warning message or change the text field background color, just a visual hint to let him know the input it's wrong.
Like you see on the pictures above, I want to show a warning sign every time the user inputs a forbidden value in the text field.
I have been reading a lot of the Apple's documentation but I don't understand which delegate to implement (NSTextFieldDelegate, NSTextDelegate, or NSTextViewDelegate), also, I have no idea how to implement it in my AppDelegate.m file and which method use and how to get the notification of user editing.
Right now, I already set the Delegate in my init method with something like this [self.textField setDelegate:self]; but I don't understand how to use it or which method implements.
I found a solution using the information posted in this question... Listen to a value change of my text field
First of all I have to declare the NSTextFieldDelegate in the AppDelegate.h file
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTextFieldDelegate>
After that, I have to instantiate the delegate for the NSTextField object I want to modify while the user update it in the AppDelegate.m file.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self.textField setDelegate:self];
}
Finally, I implement the methods to detect field editing with the changes I want to set.
- (void)controlTextDidChange:(NSNotification *)notification {
NSTextField *textField = [notification object];
if ([textField doubleValue] < 0 | [textField doubleValue] > 255) {
textField.textColor = [NSColor redColor];
}
}
- (void)controlTextDidEndEditing:(NSNotification *)notification {
NSTextField *textField = [notification object];
if ([textField resignFirstResponder]) {
textField.textColor = [NSColor blackColor];
}
}
Make your class conform to the NSTextFieldDelegate protocol. It need's to be that protocol because in the documentation it says the type of protocol the delegate conforms to.
#interface MyClass : NSObject
And implement the delegate's methods (just add them to your code). Example
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
{
}
EDIT:
I think in your case it would be better to replace the TextField for a TextView and use a NSTextViewDelegate, in the delegate, the method of most interst of you should be
- (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
{
BOOL isValid = ... // Check here if replacementString is valid (only digits, ...)
return isValid; // If you return false, the user edition is cancelled
}

Objective-C - Scrollview Events

I found some answer that quite solve my questions, but seems that I'm missing some steps. I'm just trying to intercept the "ViewDidBlablabla" events of a ScrollView, so I created a custom UIScrollView class.
MyScrollView.m
#import "MyScrollView.h"
#implementation MyScrollView
- (id)initWithFrame:(CGRect)frame{
NSLog(#"initWithFrame");
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)scrollViewWillBeginDragging:(UIScrollView*)scrollView{
NSLog(#"scrollViewWillBeginDragging");
//scrollView.contentOffset
}
-(void)scrollViewDidScroll:(UIScrollView*)scrollView{
NSLog(#"scrollViewDidScroll");
}
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{
NSLog(#"scrollViewDidEndScrollingAnimation");
}
#end
MyScrollView.h
#import <UIKit/UIKit.h>
#interface MyScrollView : UIScrollView <UIScrollViewDelegate>
#end
When I alloc my custom Scroll View I do get the console message "initWithFrame", but there's no way to get to the other events. What am I missing?
If you need some other pieces of code feel free to ask, but since I get to my custom "initWithFrame" method, I suppose that the error should be here.
Try setting your view as the delegate of itself:
if (self) {
// Initialization code
self.delegate = self;
}
Technically, you do not need to inherit UIScrollView to respond to scrolling events: it is sufficient to set the delegate to an implementation of <UIScrollViewDelegate>.
Also, the scrollViewDidEndScrollingAnimation: method has been gone for about two years, so it is not going to be called on modern iOS installations.

Displaying an UIImageView property from another class programmatically

Well, here's the situation:
I've got...
a custom class with an UIImageView-property, let's call it the Enemy-class
a ViewController
a NSMutableArray to help create multiple instances of Enemy (called enemies)
and what I want:
be able to create an unlimited amount of Enemy-Instances through a method in my ViewController (like [self spawnEnemy];, where self is the ViewController)
and, subsequently, display the UIImageView property (let's call it "enemyImage") on the view that is controlled by my ViewController
I've tried something like this:
-(Enemy *) spawnEnemy
{
Enemy *tempEnemy = [Enemy new];
[enemies addObject:(Enemy*)tempEnemy];
[self.view addSubview:(UIImageView*)[[enemies objectAtIndex:[enemies count]] enemyImage]];
//randomLocation is declared in the Enemy-Class and just assigns a random
//CGPoint to self.enemyImage.center
[[enemies objectAtIndex:[enemies count]] randomLocation];
return [[enemies objectAtIndex:[enemies count]]createEnemy];
}
This runs without errors, randomLocation gets called (tried with NSLog), AND if I do something like this in another Method of ViewController:
[[self spawnEnemy] enemyTestMethod];
enemyTestMethod is being executed as well.
But still, no enemieViews are displayed on the screen...
What am I doing wrong?
Thank you so much for your help and time.
==== Edit ====
Here's the relevant code from Enemy.h/Enemy.m:
#interface Enemy : NSObject
{
UIImageView *enemyImage;
}
#property (nonatomic, retain) IBOutlet UIImageView *enemyImage;
-(Enemy*) createEnemy;
//Enemy.m
#implementation Enemy
#synthesize enemyImage, speed;
-(Enemy *) createEnemy
{
self.enemyImage = [[UIImageView alloc] init];
[self.enemyImage setImage:[UIImage imageNamed:#"enemy.png"]];
return self;
}
I also corrected the last line in the spawnEnemy-Method to properly send createEnemy.
You don't include the code in Enemy where you alloc/init the UIImageView property. Unless this code explicitly specifies a CGRect with the size and origin that you want, the view will be initialized with CGRectZero, which means even if you're correctly adding the subview (and it looks like you are) you still won't see it anywhere.
Post the Enemy code, and the problem will probably be immediately apparent.
Have you called -createEnemy before you added them to your view?
OK, you've got this checked.
Then maybe you should check as #MusiGenesis suggested.
To do this, you need to inspect the properties of your enemyImage.
You can do this in either of the following ways:
print its frame.size by:
NSLog(#"enemyImage frame size: %#", NSStringFromCGRect(enemy.enemyImage));
set a breakpoint where you feel great, and check with your debugger:
p (CGRect)[[enemy enemyImage] frame]

Handling multiple UISwitch controls in a table view without using tag property

I have a table view controller with multiple UISwitch controls in them. I set the delegate to the table view controller with the same action for all switches. I need to be able to determine what switch was changed, so I create an array of strings that contains the name of each switch. The indexes in the array will be put in the tag property of each UISwitch.
However, I'm ready using the tag property for something else, namely to find the right control in the cell in cellForRowAtIndexPath with viewWithTag! (There are several things I need to set within each cell.)
So, am I thinking along the right lines here? I feel I'm rather limited in how I find out exactly which UISwitch changed its value, so I can do something useful with it.
I fixed this by subclassing UISwitch like so:
#interface NamedUISwitch : UISwitch {
NSString *name;
}
It seems elegant (no index arrays required) and the tag property is free to do whatever it wants.
I read that you have to be careful with subclassing in Objective-C, though...
I have written a UISwitch subclass with a block based hander for value change control events which can help when trying to track which switch's value has changed. Ideally, we could do something similar with composition rather than subclassing, but this works well for my needs.
https://gist.github.com/3958325
You can use it like this:
ZUISwitch *mySwitch = [ZUISwitch alloc] init];
[mySwitch onValueChange:^(UISwitch *uiSwitch) {
if (uiSwitch.on) {
// do something
} else {
// do something else
}
}];
You can also use it from a XIB file, by dragging a switch onto your view, and then changing its class to ZUISwitch
You are close in your approach. What I have done in similar situations is create separate UITableViewCell subclasses, set the tag of the UISwitch to be the index.row of the index path, and only use that UITableViewCell subclass in a specific section of the table view. This allows you to use the tag of the cell to uniquely determine what cell has the event without maintaining a separate index list (as it sounds like you are doing).
Because the cell type is unique, you can than easily access the other elements of the cell by creating methods/properties on the UITableViewCell Subclass.
For example:
#interface TableViewToggleCell : UITableViewCell {
IBOutlet UILabel *toggleNameLabel;
IBOutlet UILabel *detailedTextLabel;
IBOutlet UISwitch *toggle;
NSNumber *value;
id owner;
}
#property (nonatomic, retain) UILabel *toggleNameLabel;
#property (nonatomic, retain) UILabel *detailedTextLabel;
#property (nonatomic, retain) UISwitch *toggle;
#property (nonatomic, retain) id owner;
-(void) setLable:(NSString*)aString;
-(void) setValue:(NSNumber*)aNum;
-(NSNumber*)value;
-(void) setTagOnToggle:(NSInteger)aTag;
-(IBAction)toggleValue:(id)sender;
#end
In:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ... prior iniitalization code for creating cell is assumed
toggleCell.owner = self;
[toggleCell setLable:#"some string value"];
[toggleCell setTagOnToggle:indexPath.row];
toggleCell.owner = self;
return toggleCell;
//... handle cell set up for other cell types as needed
}
Owner is the delegate for the cell and can then be used to initiate actions in your controller. Make sure you connect your UISwitch to the toggleValue Action, so that you can initiate actions in the delegate when the UISwitch changes state:
-(IBAction)toggleValue:(id)sender;
{
BOOL oldValue = [value boolValue];
[value release];
value = [[NSNumber numberWithBool:!oldValue] retain];
[owner performSelector:#selector(someAction:) withObject:toggle];
}
By passing the UISwitch with the method call, you can then access the index path for the cell. You could also bypass the use of the tag property by explicitly having an ivar to store the NSIndexPath of the cell and then passing the whole cell with the method call.
I realize I'm about three years late to the party but I've developed a solution without subclassing that I think is preferable (and simpler). I'm working with the exact same scenario as Thaurin's described scenario.
- (void)toggleSwitch:(id) sender
{
// declare the switch by its type based on the sender element
UISwitch *switchIsPressed = (UISwitch *)sender;
// get the indexPath of the cell containing the switch
NSIndexPath *indexPath = [self indexPathForCellContainingView:switchIsPressed];
// look up the value of the item that is referenced by the switch - this
// is from my datasource for the table view
NSString *elementId = [dataSourceArray objectAtIndex:indexPath.row];
}
Then you want to declare the method shown above, indexPathForCellContainingView. This is a seemingly needless method because it would appear at first glance that all you have to do is identify the switch's superview but there is a difference between the superviews of ios7 and earlier versions, so this handles all:
- (NSIndexPath *)indexPathForCellContainingView:(UIView *)view {
while (view != nil) {
if ([view isKindOfClass:[UITableViewCell class]]) {
return [self.myTableView indexPathForCell:(UITableViewCell *)view];
} else {
view = [view superview];
}
}
return nil;
}