Simple bool switching not working? - objective-c

I am flipping the value of a simple BOOL like this:
active = !active;
NSLog(#"%#", active? #"active" : #"not active");
I've done this numerous times but right now, for some reason it's not working and it's really pissing me off. When ever the flow hits here, it always prints "active". It's not switching!!
Here's my interface:
#interface HeaderView : UIView {
UILabel *label;
UIButton *button;
UIImageView *background;
BOOL active;
int section;
__weak id<HeaderViewDelegate> delegate;
}
and function where the action is performed:
- (void)actionTUI:(id)sender {
active = !active;
NSLog(#"%#", active? #"active" : #"not active");
UIImage *image = nil;
if (active) {
image = [UIImage imageNamed:#"active.png"];
} else {
image = [UIImage imageNamed:#"unactive.png"];
}
[button setBackgroundImage:image forState:UIControlStateNormal];
[delegate headerView:self toggled:active];
}
Thanks

BOOLean values, as with all other types, are initialized to NULL (0, FALSE, NO) at runtime unless assigned a different value. You are assigning a not(NO) to your BOOL, which prints YES at every run.
Also, your NSLog() is weird, it's probably evaluating itself the wrong way, and most likely contributing to the problem. It should not include the literal:
NSLog(active? #"active" : #"not active");

Ok, the problem was that after toggling, I was reloading the tableview which apparently recalls:
- (UIView *)tableView:(UITableView *)_tableView viewForHeaderInSection:(NSInteger)section {
which caused a new header view to be allocated/initialized with active set to NO and right after toggling, set to YES. That's why every time, even after flipping the values it was set to active (because right after, it would get discarded and a new header view would come to it's place).

Related

How to detect change in UISegmentedControl from a separate IBAction

I have a UISegmentedControl button with three segments.
In ViewController.m this is working just fine -- pressing the buttons fires the correct methods.
I have another separate UIButton that when it is pressed it needs to first CHECK the state of the UISegmentedControl (to see which button is currently pressed) and then fire a method according to that segment value.
Here is my code for that separate UIButton. The button itself is working, but I cannot seem to figure out how to GET the current value of the segment of the UISegmentedControl.
Many thanks for any assistance here.
I am new to OBJ-C. I know how to do this in VisualBasic, so answers that are on the more verbose side would be most appreciated as I need to know the 'why'. Thank you.
- (IBAction)decodeButton:(id)sender {
UISegmentedControl *segment = [UISegmentedControl alloc]; // THIS DOES NOT WORK.
if (segment.selectedSegmentIndex == 0) {
decode(textToDecode);
} else if(segment.selectedSegmentIndex == 1) {
decode1(textToDecode);
} else if(segment.selectedSegmentIndex == 2) {
decode2(textToDecode);
}
}
Here is a Tutorial using UISegmentedControl in iOS.
Just Create a Reference object and wire it properly to File Owner.
IBOutlet UISegmentedControl *segmentedControl;
Then set property
#property (strong, nonatomic) IBOutlet UISegmentedControl * segmentedControl;
Synthesize in .m file
#synthesize segmentedControl;
Now You can Access the selected index at any time.
- (IBAction)decodeButton:(id)sender {
if (segmentedControl.selectedSegmentIndex == 0) {
decode(textToDecode);
} else if(segmentedControl.selectedSegmentIndex == 1) {
decode1(textToDecode);
} else if(segmentedControl.selectedSegmentIndex == 2) {
decode2(textToDecode);
}
}
Your code alloc every time UISegmentedControl in the button press action. So use the following code for sUISegmentedControl creation and its action .
SegmentChangeView=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Segment1",#"Segment2",#"Segment3",nil]];
SegmentChangeView.frame=CGRectMake(5, 44, self.view.bounds.size.width-10, 33);
SegmentChangeView.selectedSegmentIndex=0;
SegmentChangeView.segmentedControlStyle=UISegmentedControlStyleBar;
SegmentChangeView.momentary = YES;
[SegmentChangeView setTintColor:[UIColor blackColor]];
NSDictionary *attributes =[NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:#"Arial" size:13],UITextAttributeFont,nil];
[SegmentChangeView setTitleTextAttributes:attributes forState:UIControlStateNormal];
[SegmentChangeView addTarget:self action:#selector(SegmentChangeViewValueChanged:) forControlEvents:UIControlEventValueChanged];
SegmentChangeView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleBottomMargin;
[self.view addSubview:SegmentChangeView];
-(IBAction)SegmentChangeViewValueChanged:(UISegmentedControl *)SControl
{
if (SControl.selectedSegmentIndex==0)
{
decode(textToDecode);
}
else if (SControl.selectedSegmentIndex==1)
{
decode1(textToDecode);
}
else if (SControl.selectedSegmentIndex==2)
{
decode2(textToDecode);
}
}
You should remove UISegmentedControl *segment = [UISegmentedControl alloc] ; from your code, as it allocs anew instance of your UISegmentedControl every time, instead,
create an outlet for you UISegmentController like
#property (strong, nonatomic) IBOutlet UISegmentedControl * segment;
and then later at any point in your viewcontroller.m file, you can get the currently selected segment by using
segment.selectedSegmentIndex;
Hope this make sense,
Regards
Try like this
- (IBAction)segmentedControlChanged:(id)sender
{
UISegmentedControl *s = (UISegmentedControl *)sender;
if (s.selectedSegmentIndex == 1)
{
//code
}
else
{
//code
}
}
This code means you are creating a new Object on every click
UISegmentedControl *segment = [UISegmentedControl alloc] ;
The thing you have to do take IBOutlet (Property) of your segmentedControl then I will work for you. dont create a new object in the button method. when you will make a IBOutlet it will be link with at segmentControl and your code will work that time . Thanks

Change MKAnnotationView programmatically

Is there a way to change MKAnnotationView style (like from red label with number to green colored label with number).
I want to change this style according to distance from target. My annotation is moving, with user.
I dont want to use remove / add annotation, because it causes "blinking".
Can it be done someway?
UPDATE:
I am adding code, how I am doing it right now
MKAnnotationView *av = [mapView viewForAnnotation:an];
if ([data->type isMemberOfClass:[UserAnnotationImage class]])
{
UIImage *img = [UIImage imageNamed: ((UserAnnotationImage *)data->type)->url];
[av setImage:img];
}
else if ([data->type isMemberOfClass:[UserAnnotationLabel class]])
{
UIView * v = [av viewWithTag:0];
v = ((UserAnnotationLabel *)data->type)->lbl;
av.frame = ((UserAnnotationLabel *)data->type)->lbl.frame;
}
else if ([data->type isMemberOfClass:[UserAnnotationView class]])
{
UIView * v = [av viewWithTag:0];
v = ((UserAnnotationView *)data->type)->view;
av.frame = ((UserAnnotationView *)data->type)->view.frame;
}
Sadly, its not working :(
Yes, basically you get a reference to the annotation view and update its contents directly.
Another way, if you have a custom annotation view class, is to have the annotation view monitor the changes it is interested in (or have something outside tell it) and update itself.
The first approach is simpler if you are using a plain MKAnnotationView or MKPinAnnotationView.
Wherever you detect that a change to the view is needed, get a reference to the view by calling the map view's viewForAnnotation instance method. This is not the same as calling the viewForAnnotation delegate method.
Once you have a reference to the view, you can modify as needed and the changes should appear immediately.
An important point is that the logic you use to update the view outside the delegate method and the logic you have in the viewForAnnotation delegate method must match. This is because the delegate method may get called later (after you've updated the view manually) by the map view and when it does, the code there should take the updated data into account.
The best way to do that is to have the annotation view construction code in a common method called both by the delegate method and where you update the view manually.
See change UIImage from MKAnnotation in the MKMapView for an example that updates just the annotation view's image.
For an example (mostly an idea for an approach) of updating the view using a custom annotation view class, see iPad Mapkit - Change title of "Current Location" which updates the view's pin color periodically (green, purple, red, green, purple, red, etc).
There are too many unknowns in your code to explain why it doesn't work. For example:
What is data? Is it annotation-specific (is it related to an)? What is type? Does it change after the annotation has been added to the map?
Why is data storing entire view objects like a UILabel or UIView instead of just the underlying data that you want to show in those views?
imageNamed requires the image to be a resource in the project (not any arbitrary url)
Don't use a tag of 0 (that's the default for all views). Start numbering from 1.
You get a view using viewWithTag but then replace it immediately with another view.
I'll instead give a more detailed but simple(r) example...
Assume you have an annotation class (the one that implements MKAnnotation) with these properties (in addition to coordinate, title, and subtitle):
#property (nonatomic, assign) BOOL haveImage;
#property (nonatomic, copy) NSString *labelText;
#property (nonatomic, copy) NSString *imageName;
#property (nonatomic, assign) CLLocationDistance distanceFromTarget;
To address the "important point" mentioned above (that the viewForAnnotation delegate method and the view-update-code should use the same logic), we'll create a method that is passed an annotation view and configures it as needed based on the annotation's properties. This method will then be called both by the viewForAnnotation delegate method and the code that manually updates the view when the annotation's properties change.
In this example, I made it so that the annotation view shows the image if haveImage is YES otherwise it shows the label. Additionally, the label's background color is based on distanceFromTarget:
-(void)configureAnnotationView:(MKAnnotationView *)av
{
MyAnnotationClass *myAnn = (MyAnnotationClass *)av.annotation;
UILabel *labelView = (UILabel *)[av viewWithTag:1];
if (myAnn.haveImage)
{
//show image and remove label...
av.image = [UIImage imageNamed:myAnn.imageName];
[labelView removeFromSuperview];
}
else
{
//remove image and show label...
av.image = nil;
if (labelView == nil)
{
//create and add label...
labelView = [[[UILabel alloc]
initWithFrame:CGRectMake(0, 0, 50, 30)] autorelease];
labelView.tag = 1;
labelView.textColor = [UIColor whiteColor];
[av addSubview:labelView];
}
if (myAnn.distanceFromTarget > 100)
labelView.backgroundColor = [UIColor redColor];
else
labelView.backgroundColor = [UIColor greenColor];
labelView.text = myAnn.labelText;
}
}
The viewForAnnotation delegate method would look like this:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MyAnnotationClass class]])
{
static NSString *myAnnId = #"myann";
MKAnnotationView *av = [mapView dequeueReusableAnnotationViewWithIdentifier:myAnnId];
if (av == nil)
{
av = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:myAnnId] autorelease];
}
else
{
av.annotation = annotation;
}
[self configureAnnotationView:av];
return av;
}
return nil;
}
Finally, the place where the annotation's properties change and where you want to update the annotation view, the code would look something like this:
ann.coordinate = someNewCoordinate;
ann.distanceFromTarget = theDistanceFromTarget;
ann.labelText = someNewText;
ann.haveImage = YES or NO;
ann.imageName = someImageName;
MKAnnotationView *av = [mapView viewForAnnotation:ann];
[self configureAnnotationView:av];

hide or show tableview

Say I have a switch and a small table view(no scroll) below it. I know if the switch is turned on/off using a bool switchState whose value get changed in the action method of the switch:
-(IBAction)switchSlide:(id)sender{
if (toggleSwitch.on == YES) {
switchState = YES;
}
else{
switchState = NO;
}
}
Now what I want is that the table view below it should hide when the switchState == NO. How do I do that?
Every UIView has a property hidden:
#property(nonatomic, getter=isHidden) BOOL hidden
since a UITableView is a sublass of UIView you can use the methods from a UIView too.
So your code just need a little adjustment (assuming you are calling this IBAction in a UITableViewController):
-(IBAction)switchSlide:(id)sender{
if (toggleSwitch.on == YES) {
switchState = YES;
self.tableView.hidden = NO;
}
else{
switchState = NO;
self.tableView.hidden = YES;
}
}
Edit:
Solved this via chat and the solution is:
Since you used a UIViewController you have to make a propert for the UITableView. synthesize it and connect the outlet by dragging from the files owner to the UITableView in the interface builder. Now you can use the code above.

Using a UIButton to update the image in a UIImageView

I'm really new to app development and I am building an app (that will be more complex but this is just the basis of it which I will later expand). What I have currently is view with a UIImageView and UIButton, code below. I can get the button to set an image from the resources folder, but what I want is for the button to update the UIImageView with a new image each time it is press e.g. press once = image1, pressed twice = image2...etc From what I have been reading I think I should have the images in an array with a key or something then I can just update it with ++ I know this is really vague but as I said I'm really new so any help would be greatly appreciated!
#interface NextImageViewController : UIViewController {
IBOutlet UIImageView *imageView;
}
- (IBAction) nextImagePush;
#end
#implementation NextImageViewController
- (IBAction) nextImagePush {
UIImage *img = [UIImage imageNamed:#"image1"];
[imageView setImage:img];
}
#interface NextImageViewController : UIViewController {
IBOutlet UIImageView *imageView;
NSInteger imageCount;
NSArray * imageArray;
}
- (IBAction) nextImagePush;
#end
#implementation NextImageViewController
- (IBAction) nextImagePush {
UIImage *img = [UIImage imageNamed:[imageArray objectAtIndex:imageCount]];
[imageView setImage:img];
imageCount++;
if (imageCount >= [imageArray count]){
imageCount = 0;
}
}
- (void) viewDidLoad {
imageArray = [[NSArray alloc]
initWithObjects:#"image1", #"image2", #"image3", nil];
imageCount = 0;
}
Try something like that. You wil have to set the initial image and change the count depending on what you set it.
You can simply create a NSArray or NSMutableArray and fill it with all the images you want to show. Then, using a variable visible at global scope (ie a field of your interface), you can use
[imageView setImage:[arrayOfImages objectAtIndex:(index++ % [arrayOfImages count])]];
This code will also show the first image after the last one is displayed.
A really rudimentary way to do this is set a integer. Define it as 0,
and the first time you click the button you add 1 to it.
This definitely isn't working code, but its just to help you get a start. I strongly suggest a book like Big Nerd Ranch's book on iOS or Beginning IPhone 4 Development.
-(IBAction)pic
if variable == 0 {
//show the first image
variable++;
}
else if variable == 1 {
//show the second image
variable++;
}

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.