setState not updating a checkbox's view - objective-c

I'm trying to create a preferences window.
In it I have some checkbox style NSButtons. The problem is that they aren't updating when I call the setState: method. I access the standarUserDefaults when then window is initialized to get the state they should be in and was planning to change them depending on what state that key is in.
I know that they are in fact connected to both their IBOutlet and IBAction as I've tried some NSLog-ing to make sure of that.
I read something about Changing the value of a model property programmatically is not reflected in the user interface here but I'm not sure if that's the problem or frankly what they are referring to there.
I declared the checkbuttons in the .h file like so:
IBOutlet NSButton *defaultDateCheck;
IBOutlet NSButton *closeOnCreationCheck;
IBOutlet NSButton *allowEmptyNumberCheck;
IBOutlet NSPopUpButton *defaultJobSetting;
The init method looks like this:
-(id)initWithWindowNibName:(NSString *)windowNibName{
self = [super initWithWindowNibName:windowNibName];
if (self) {
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
[defaultDateCheck setState:[myDefaults boolForKey:#"useDefaultDate"]];
[closeOnCreationCheck setState:[myDefaults boolForKey:#"closeOnCreation"]];
[allowEmptyNumberCheck setState:[myDefaults boolForKey:#"allowEmptyProjectNumber"]];
[defaultJobSetting selectItemAtIndex:[myDefaults integerForKey:#"defaultJob"]];
}
return self;
}
I also tried the following format for setting the checkboxes but with no result:
if ([myDefaults integerForKey:#"useDefaultDate"] == YES) {
[defaultDateCheck setState:NSOnState];
}
else {
[defaultDateCheck setState:NSOffState];
}
The connected IBAction methods for the checkboxes looks like this:
-(IBAction)toggleCloseOnCreation:(id)sender{
[[NSUserDefaults standardUserDefaults] setBool:([closeOnCreationCheck state] == NSOnState) forKey:#"closeOnCreation"];
[[NSUserDefaults standardUserDefaults] synchronize];
}

Set the state in -viewDidLoad method since in -init the view and it's subviews do not exist yet

When setting the value of your default use:
[myDefaults setObject:[NSNumber numberWithBool:YES] forKey:#"YourKey"];
When checking to see if the value is true or false, use:
[[[NSUserDefaults standardUserDefaults] objectForKey:#"YourKey"] boolValue]

Related

Programmatically change state of a UISwitch bugs

I'm trying to dynamically change a UISwitch with the method [self.mySwitch setOn:YES animated:YES];
The state change as well in the code so that the mecanisme is working fine but in the view the state has not change. So I get a UISwitch shown as OFF and working as it was as ON.
When I tap on it, the switch became ON. So I have to tap it twice to launch the inCaseOff part of code.
I hope this is clear enough.
[EDIT]
This is the code you have asked
- (void)viewDidLoad
{
[self manageTheSwitch];
}
- (void) manageTheSwitch{
self.mySwitch = [[UISwitch alloc]init];
if(randomObject != nil){
[self.mySwitch setOn:YES animated:YES];
}else{
[self.mySwitch setOn:NO animated:YES];
}
}
You're programmatically setting a different UISwitch to the one shown in your view. You shouldn't have to do [[UISwitch alloc]init] at all, instead you should retrieve it through an IBOutlet property in your controller (wired up to your view in IB).
Assuming you did wire up mySwitch, then all you need to do is remove this line:
self.mySwitch = [[UISwitch alloc]init];
Your problem is, that you're instantiating a new button in your manageTheSwitch method rather than accessing the one you created in the storyboard. Just eliminate that alloc init.
try using the "selected" property UISwitch inherits from UIControl:
self.mySwitch.selected = YES;

Cannot access variable from another viewController

I have one viewController called "setTimeViewController". I have another view controller called "setEventViewController." users tap a row in a table in setTimeViewController and are sent to setEventViewController that only contains a UIPickerView and a save button. When the user taps the save button, it takes them back to setTimeViewController.
I want the value that is chosen in that picker from setEventViewController to be able to be accessed from setTimeViewController but is returning (null) to me. I have declared a NSString *theVariable in .h setEventViewController which is the variable I am trying to retrieve from the other view controller and retained its property but it is still null.
I have done a test (NSLog the variable) from viewDidDisappear in setEventViewController to see if it is null when the view is disappearing but it works as it should.
Here is my code, if anyone can help me I would forever be grateful. Thank you!
setEventViewController.m
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
theVariable= [currentItemsInPicker objectAtIndex:row];
//[theVariable retain]; //Tried this as well but did not work
}
-(IBAction) Save
{
//simply dismisses the view controller back to setTimeViewController. Have also tried to set another NSString equal to theVariable but this did not work.
[self.navigationController popViewControllerAnimated:YES];
}
setTimeViewController
-(void) retrieveTheEvent
{
setEventViewController *eventViewController= [[setEventViewController alloc] init];
NSString *testString= eventViewController.theVariable;
NSLog (#"the event is %#", testString); //shows null
}
You are allocating different object of setEventViewController in retrieveTheEvent if I am not wrong. You are facing this problem because this newly allocated object is different than you have pushed.
Instead of use the same object that you have push to navigation controller.
One solution:
Create global object your setEventViewController(i.e. I mean create iVar for it) and use same reference to push view controller in didSelectRow. And use same iVar for accessing your theVariable.
Add below code in setTimeViewController.h
setEventViewController *eventViewController;
Please also create property for it.
Now in setTimeViewController.m
Now use existing reference of setEventViewController to push view controller. like
eventViewController= [[setEventViewController alloc] init];
[self.navigationController pushViewController: eventViewController animated:YES];
Change this method
-(void)retrieveTheEvent
 {
  NSString *testString= eventViewController.theVariable;
  NSLog (#"the event is %#", testString); //shows null
  }
Adding another solution to Armaan's list.
Create a delegate in setEventViewController.m and pass "theVariable" to setTimeViewController.m before calling
[self.navigationController popViewControllerAnimated:YES];
I'm giving an example.
setEventViewController.h
#protocol setEventViewControllerDelegate;
#interface setEventViewController : UIViewController
{
NSString* theVariable;
id<setEventViewControllerDelegate> delegate;
}
#end
#protocol setEventViewControllerDelegate <NSObject>
#optional
-(void)theVariableChanged:(NSString*)theNewValue;
#end
setEventViewController.m
#synthesize delegate;
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
theVariable= [currentItemsInPicker objectAtIndex:row];
// this is where the new value is passed to setTimeViewController
if(delegate && [delegate respondsToSelector:#selector(theVariableChanged)])
{
[delegate theVariableChanged:theVariable];
}
}
-(IBAction) Save
{
[self.navigationController popViewControllerAnimated:YES];
}
-(void) dealloc
{
// very important
self.delegate = nil;
}
setTimeViewController.h
#import "setEventViewController.h"
#interface setTimeViewController : UIViewController <setEventViewControllerDelegate>
{
// your members
}
#end
setTimeViewController.m
-(void)openSetEventView
{
setEventViewController *eventViewController= [[setEventViewController alloc] init];
// set the delegate
eventViewController.delegate = self;
[self.navigationController pushViewController: eventViewController animated:YES];
[eventViewController release];
}
// get the new value here
-(void)theVariableChanged:(NSString*)theNewValue
{
NSLog (#"the event is %#", theNewValue);
}
Check out Singletons,
They can be your best friend when doing something like this.
Singletons link here
I tried all different suggestion to pass NSString from one viewController to the other, including customer initialise method, and change the property to strong, retain, non of them work for me.
At last singleton to share the data across the project fixed the problem. http://www.galloway.me.uk/tutorials/singleton-classes/
Thanks #David Evans

Save preference to show or hide NSStatusItem

I've got an application which runs as a normal app but also has a NSStausItem.
I wanted to implement the ability to set in the preferences a checkbox and when this checkbox is turned on the status item should be shown, but when the checkbox is off the status item should be removed or be invisible.
I found someone facing a similar problem in a forum here: How do you toggle the status item in the menubar on and off using a checkbox?
But the problem I have with this solution is that it does not work as expected. So I make this checkbox and all works fine, but when I open the application a second time the app does not recognize the choice I took at the first run. This is because the checkbox isn't bound to a BOOL or something, the checkbox only has an IBAction, which removes or adds the status item at runtime.
So my question is: how can I make a checkbox in the preferences which allows me to choose whether the status item should show up or not.
Ok actually i tried the following i copied the from the post i gave you the link
In AppDelegate.h :
NSStatusItem *item;
NSMenu *menu;
IBOutlet NSButton myStatusItemCheckbox;
and then in the Delegate.m :
- (BOOL)createStatusItem
{
NSStatusBar *bar = [NSStatusBar systemStatusBar];
//Replace NSVariableStatusItemLength with NSSquareStatusItemLength if you
//want the item to be square
item = [bar statusItemWithLength:NSVariableStatusItemLength];
if(!item)
return NO;
//As noted in the docs, the item must be retained as the receiver does not
//retain the item, so otherwise will be deallocated
[item retain];
//Set the properties of the item
[item setTitle:#"MenuItem"];
[item setHighlightMode:YES];
//If you want a menu to be shown when the user clicks on the item
[item setMenu:menu]; //Assuming 'menu' is a pointer to an NSMenu instance
return YES;
}
- (void)removeStatusItem
{
NSStatusBar *bar = [NSStatusBar systemStatusBar];
[bar removeStatusItem:item];
[item release];
}
- (IBAction)toggleStatusItem:(id)sender
{
BOOL checked = [sender state];
if(checked) {
BOOL createItem = [self createStatusItem];
if(!createItem) {
//Throw an error
[sender setState:NO];
}
}
else
[self removeStatusItem];
}
then in the IBaction i added this one :
[[NSUserDefaults standardUserDefaults] setInteger:[sender state]
forKey:#"MyApp_ShouldShowStatusItem"];
and in my awakefromnib i added this one : `
NSInteger statusItemState = [[NSUserDefaults standardUserDefaults] integerForKey:#"MyApp_ShouldShowStatusItem"];
[myStatusItemCheckbox setState:statusItemState];
Then in the interface builder i created a new checkbox connected it with "myStatusItemCheckbox" and added an IBaction also i clicked on the bindings inspector and set in the value the following bind to : NSUserDefaultController and as ModelKeyPath i set: MyApp_ShouldShowStatusItem.
Unfortunately this doesnt work at all what am i doing wrong ?
What you need to do is to use the User Defaults system. It makes it very easy to save and load preferences.
In the button's action, you will save its state:
- (IBAction)toggleStatusItem:(id)sender {
// Your existing code...
// A button's state is actually an NSInteger, not a BOOL, but
// you can save it that way if you prefer
[[NSUserDefaults standardUserDefaults] setInteger:[sender state]
forKey:#"MyApp_ShouldShowStatusItem"];
}
and in your app delegate's (or another appropriate object) awakeFromNib, you will read that value back out of the user defaults:
NSInteger statusItemState = [[NSUserDefaults standardUserDefaults] integerForKey:#"MyApp_ShouldShowStatusItem"];
[myStatusItemCheckbox setState:statusItemState];
and then make sure to call removeStatusItem if neccessary.
This procedure will apply to almost any preference you might want to save.

Working backwards from null

I know that once you get better at coding you know what variables are and null popping out here and there may not occur. On the way to that state of mind are there any methods to corner your variable that's claiming to be null and verify that it is indeed null, or you just using the wrong code?
Example:
-(IBAction) startMotion: (id)sender {
NSLog(#"Forward or back button is being pressed.");
UIButton * buttonName = (UIButton *) sender;
NSLog(#"Button Name: %#", buttonName.currentTitle);
}
Button Name: (null) is what shows up in the console
Thanks
According to Apple's docs, the value for currentTitle may be nil. It may just not be set.
You can always do if (myObject == nil) to check, or in this case:
-(IBAction) startMotion: (id)sender {
NSLog(#"Forward or back button is being pressed.");
UIButton * buttonName = (UIButton *) sender;
if (buttonName != nil) {
NSString *title = buttonName.currentTitle;
NSLog(#"Button Name: %#", title);
}
}
Another way to check if the back or forward button is pressed, is check the id itself.
//in the interface, and connect it up in IB
//IBOutlet UIButton *fwdButton;
//IBOutlet UIButton *bckButton;
-(IBAction) startMotion: (id)sender {
NSLog(#"Forward or back button is being pressed.");
UIButton * buttonName = (UIButton *) sender;
if (buttonName == fwdButton) {
NSLog(#"FWD Button");
}
if (buttonName == bckButton) {
NSLog(#"BCK Button");
}
}
also, make sure your outlets and actions are all connected in IB, and that you save and re-build the project. I've gone where I changed somehting in IB, saved the .m file (not the nib) and was like "why isn't this working???"
I was using the wrong field in Interface Builder I was using Name from the Interface Builder Identity instead of Title from the button settings.
buttonName cannot be null, otherwise buttonName.currentTitle would produce an error.
Therefore the currentTitle attribute itself must be null.
Or, maybe currentTitle is a string with the value (null).
In general, in Objective-C, if you have [[[myObject aMethod] anotherMethod] xyz] and the result is null it's difficult to know which method returned null. But with the dot syntax . that's not the case.

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;
}