Change UISegmentedControl selected index or value programmatically - ios

I have a UISegmentedControl with two segments (index: 0 and 1) that I am trying to reset programmatically to 0. When I run the command below it does not reset the segment index to 0 as expected. Instead it highlights segment indexed 1.
[seg setSelectedSegmentIndex:0];
Oddly when I log the selected segment, it returns 0.
NSLog(#"seg setSelectedSegmentIndex %d", seg.selectedSegmentIndex);
Also oddly, after running setSelectedSegmentIndex:0 I cannot reselect segment 0 manually by touch, it seems to be locked to segment 1, until I tap 1 wherein it works as normal.
Here is the setup for the button:
itemArray = [NSMutableArray arrayWithObjects: #"Zero", #"One", nil];
seg = [[UISegmentedControl alloc] initWithItems:itemArray];
[seg setFrame:segRect];
seg.segmentedControlStyle = UISegmentedControlStyleBar;
seg.momentary = NO;
[seg addTarget:self action:#selector(someAction:) forControlEvents: UIControlEventValueChanged];
[mainView addSubview:seg];
NOTE: To restate what I was trying to accomplish with this question: I want to set a UISegmentedControl's selected index via a variable dynamically, get the appropriate visual feedback, and check the selected index in a method, to make something happen depending on the selected index. See my answer below for a solution.

Change in viewDidLoad:
[seg setSelectedSegmentIndex:index];
you change use the index value as your wish.

From code, you can just do seg.selectedSegmentIndex = someDefaultIndex.
Whether you should set it in viewDidLoad: or not depends entirely on the structure of your application. For example, if your app is starting up and loading the view for the first time and needs to set the control to whatever value it had during the previous run of the app, then it definitely makes sense to do it there.

Swift 3.0
segmentedControl.selectedSegmentIndex = #your value#

The correct answer is the #arcady bob one at this link.
I have posted an updated version for Swift 5, which I quote here:
yourSegmentedControl.selectedSegmentIndex = 0
yourSegmentedControl.sendActions(for: UIControl.Event.valueChanged)
If you use just the first line, the segmented will react accordingly graphically, but your IBAction associated with the segmented control won't be called. In a few words: the second line is like tapping on the segmented control. This is what I needed and what I was missing.

This helps sort the selected segment, in terms of the visual feedbak, that is after programatically setting the selected segment this will tint the segments properly.
for (int i=0; i<[seg.subviews count]; i++)
{
if ([[seg.subviews objectAtIndex:i] respondsToSelector:#selector(isSelected)] && [[seg.subviews objectAtIndex:i]isSelected])
{
[[seg.subviews objectAtIndex:i] setTintColor:[UIColor grayColor]];
}
if ([[seg.subviews objectAtIndex:i] respondsToSelector:#selector(isSelected)] && ![[seg.subviews objectAtIndex:i] isSelected])
{
[[seg.subviews objectAtIndex:i] setTintColor:[UIColor blackColor]];
}
}

To restate what I was trying to accomplish with this question: I want to set a UISegmentedControl's selected index via a variable dynamically, get the appropriate visual feedback, and check the selected index in a method, to make something happen depending on the selected index.
Originally I guessed that this is as simple as: mySegmentedController.selectedSegmentIndex = someIndex, then test for the index. But as stated in my question, it was not working for me. The behavior is odd. After running that code the appropriate button would not highlight.
Here is a project where I did fix it, so it works. Not sure exactly why it works. Possibly a delay I put in there, before setting the index. I will update and comment the project file if I can refine it.
http://owolf.net/uploads/StackOverflow/SegmentedControllers.zip

WARNING: This is not quite answering the question, but is closely related.
I had a similar issue. Most of the time my UISegmentedController worked fine. It has 4 segments, Off, 30, 60 and 120.
At random intervals (normally just prior to a release!!), the Off segment becomes un-selectable from the user interface. (I didn't try selecting it from code because that is not what I need to do and won't solve my problem.)
The solution is to delete that UISegmentedController from my XIB and replace it with a fresh (and exactly identical) one which then works fine.
I even CMD-C and CMD-V'd the offending UISegmentedController into another App's XIB. And the Off segment (i.e the one at index 0) was un-selectable in that XIB too.
This seems to be an issue with XCode.

Related

Showing a correct answer when someone selects a wrong one

Okay so I am new to objective-c and have used a couple different tutorials to build my first app that is a quiz. I had it completely done and I was somewhat satisfied.. then I realized that if someone selects the wrong answer it doesn't show them what the correct one is. So I have been trying to figure it out for a little bit. So I created a new label That stays hidden until an answer is chosen.
Is there an if statement I can use that can utilize the text I set buttons to? For example:
switch (QuestionSelected) {
case 0:
QuestionText.text = [NSString stringWithFormat:#"Question One"];
[Answer1 setTitle:#"Answer number 1" forState:UIControlStateNormal];
[Answer2 setTitle:#"Answer number 2" forState:UIControlStateNormal];
[Answer3 setTitle:#"Answer number 3" forState:UIControlStateNormal];
[Answer4 setTitle:#"Answer number 4" forState:UIControlStateNormal];
Answer1Correct = YES;
break;
This is what a question looks like within the category. My Answer1-4's are the buttons. And the Answer1Correct (depending on the answer) is a BOOL statement.
Is there a line that I can use in an if statement saying something along
if (Answer1Correct == YES) {
CorrectAnswer.text = [NSstring stringWithFormat:"#?", Answer1]; (The question mark would be for text the button was set to. I know that #i, can be used for integers but I don't know if I can use the text a button was set to)
I know that won't work but I am trying to wrap my head around how a lot of the valuables work. For instance, the problem I see is that within my questions I have BOOLs, and the only way it activates the BOOL is dependent on what they choose. For instance if they chose Answer2 in that previous statement it just sets runs my wrong answer one because this is what I have for the buttons
-(IBAction)Answer1:(id)sender{
if (Answer1Correct == YES) {
[self RightAnswer];
}
else{
[self WrongAnswer];
}
And then the same thing for the following three buttons.
I know I am probably all over the place but the reason I started this was to try and learn how to understand it. So how can I establish what is Correct when they choose the wrong one and how could I make the label show accordingly. I am not asking to be spoon fed, this is my first fully functional app that doesn't really have a full purpose other than myself trying to put things together and understand how things work together. So a little explanation or where I can read up on what is going on would be more appreciated then just an answer. I probably need to give a little more information so if I what I am asking doesn't make sense please ask. Thank you guys for the help.
Without seeing all of the code it's difficult, however one approach would be to have an array with the correct values in for each of the questions you would need to set a strong reference to this to ensure that it's not dealloc'd by arc.
in your .h
#property (nonatomic, strong)NSArray *correctAnswersArray;
#property (nonatomic, strong) IBOutlet UILabel *myLabelForIncorrectAnswer;
in your .m
-(void)viewDidLoad{
self.correctAnswersArray = [[NSArray alloc]initWithObjects:#"answer one", #"answer two", #"answer three", #"answer four",nil];
}
this can be reference using
self.correctAnswersArray[i];
where 'i' is the int value you require from the array - note that arrays are indexed starting at 0
With regards to your current conditional statement, you could set the label text using the array when you confirm if the answer is correct or incorrect so
if (Answer1Correct == YES) {
[self RightAnswer];
}
else{
[self WrongAnswer];
self.myLabelForIncorrectAnswer.text = self.correctAnswersArray[0];
}
and so on for each of the conditionals amending the array index number for each 'else' condition

Continuously Update Label Based on UISlider Value

I currently have a series of 5 UISliders that continuously update their respective labels to display what value the user is selecting in an hh:mm:ss format. I have the properties of each slider set to continuous and have their targets set to when the slider is changed (This example would be for just the first slider).
sliderOne.continuous=YES;
[sliderOne addTarget:self action:#selector(sliderOneChanged:) forControlEvents:UIControlEventTouchUpInside];
Each slider's individual label updates the time selected perfectly. Now however, I need to update another label underneath all of these sliders that continuously sums up the value of the sliders.
At the same time, I am also doing some conversions with the value of the slider using two segmented controllers -- a miles/kilometers segmented controller and various distances in another segmented controller. Each conversion results in a time in seconds that is then converted back into the hh:mm:ss format and displayed in the last label when the user selects which combo from the segment controller they want. I have all the math worked out for this, and the label will update correctly when changing segments, but not continuously.
How could I update a label to continuously show the sum of every UISlider as the user changes them?
For a continuous updating, use the UIControlEventValueChanged event along with the continuous property
sliderOne.continuous=YES;
[sliderOne addTarget:self action:#selector(sliderOneChanged:) forControlEvents:UIControlEventValueChanged];
Use KVO to get notified of changes in UISliders and update the UI accordingly. You want to wrap UI updates with dispatch_async to avoid locking the interface.
Using KVO-Notification-Manager it can be something like this (in viewDidLoad or similar):
id token1 = [slider1 addKVOBlockForKeyPath:#"value" options:NSKeyValueObservingOptionNew handler:^(NSString *keyPath, id object, NSDictionary *change) {
dispatch_async(dispatch_get_main_queue(), ^{
whateverSliderNeedsUpdate.value = /* your calculation result */;
});
}];
id token2 = [slider2 /* repeat for as many sliders as you want */];
...
It's important to remove the observers at some point.
- (void)dealloc {
[slider1 removeKVOBlockForToken:token1];
[slider2 ...];
}
If you feel adventurous you can try with ReactiveCocoa.
You could create a method that does all of your math and updates your UILabel with the result. For example:
- (void)updateSumLabel {
// this is obviously very simple
float num1 = [_sliderOne value];
float num2 = [_sliderTwo value];
float sum = num1 + num2;
[_sumLabel setText:[NSString stringWithFormat:#"#f",sum]];
}
The you can call updateSumLabel from any of your existing methods like sliderOneChanged. This is probably the most simple way todo this.

Stuck with UIActionSheetPicker , iOS

This is going to be a very targeted question , as its for people that have actually used the UIActionSheetPicker before.
I used it in my iphone applications and everything worked great , but now that i tried to implement it on my ipad i experienced some problems.
The main problem is that on ipad the picker appears as a "bubble" or "info window" if you prefer , which points at the spot where it was called , like a button a text field etc.
Below is an example of it :
You can see that the "info window" is pointing at the animals button.
In my application i have 4 different buttons. 2 are in the top side of the screen and 2 at the bottom.
Those who are at the bottom , call the picker very well and the picker appears on top of the buttons pointing down on them.
However , the ones on the top of the screen (almost like the one in the image) when they call the picker the picker appears much lower on the screen and doesnt point at them as it should ...
I mean i expected to appear just under the buttons pointing at them (like in the image), but they are almost in the center of the screen pointing nowhere..
Any ideas? Has someone experienced this before?
EDIT
In the beginning , i was calling the ActionSheetPicker inside the buttons action like this:
- (IBAction)selectTopic:(UIControl *)sender {
[ActionSheetStringPicker showPickerWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
//when user picks a new topic we clean the titleField to make sure selected title of previous topic doesnt mix up with new topic
titleField.text = #"";
}
Now i am trying to call it like this:
- (IBAction)selectTopic:(UIControl *)sender {
ActionSheetStringPicker *thePicker = [[ActionSheetStringPicker alloc] initWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
thePicker.presentFromRect = topicField.frame;
[thePicker showActionSheetPicker];
//when user picks a new topic we clean the titleField to make sure selected title of previous topic doesnt mix up with new topic
titleField.text = #"";
}
Where topicField is my TextField.
Sadly the result is the same. Even now that i am specifying where i want the arrow to point , the picker is called 300 pixels down.
The strange thing is though that even with another other button that is a bit lower than the previous the picker is again exactly 300 pixels down.
EDIT2
After noticing that the picker shows 300 pixels down , i decided to manually make it show 300pixels up , to point exactly on my button.
I used the following code :
- (IBAction)selectTopic:(UIControl *)sender {
//[ActionSheetStringPicker showPickerWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
ActionSheetStringPicker *thePicker = [[ActionSheetStringPicker alloc] initWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
CGRect pickerFrame = topicField.frame;
pickerFrame.size.height -= 300;
thePicker.presentFromRect = pickerFrame;
[thePicker showActionSheetPicker];
//when user picks a new topic we clean the titleField to make sure selected title of previous topic doesnt mix up with new topic
titleField.text = #"";
}
Amazingly the button once again appears in the same position 300pixels down. I start to believe that this one may not be the property to alter the position of the picker.
Any ideas ?
Setting the presentFromRect in your calling code won't make a difference since it will be reset based on the sender within the ActionSheetPicker code, instead you are going to need to modify the source code of the library.
The following (unmerged) commit on github should resolve the issue On iPad, set the popover to point properly at its container.
Basically within the AbstractActionSheetPicker.m file modify the - (void)presentPickerForView:(UIView *)aView method like the following
- (void)presentPickerForView:(UIView *)aView {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self.presentFromRect = CGRectMake(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);
[self configureAndPresentPopoverForView:aView];
}
else {
self.presentFromRect = aView.frame;
[self configureAndPresentActionSheetForView:aView];
}
}
After a brief look at the code, I would say that you want to make sure that the presentFromRect on the ActionPicker object is in the coordinates of the container that you are passing in on the init. For instance if I had one big view that contained a button:
This is untested:
UIView* someBigView = ... // get the big view
UIButton* someButton = ... // get the button that the big view contains
ActionSheetDatePicker* thePicker = [[ActionSheetDatePicker alloc] initWithTitle:#"Some Title"
datePickerMode:UIDatePickerModeDate
selectedDate:[NSDate date]
target:self
action:#selector(someMethod:)
origin:someBigView];
//the arrow should be pointing at the button
thePicker.presentFromRect = someButton.frame;
[thePicker showActionSheetPicker];
In my opinion, this library has a pretty big flaw in the fact that it takes an origin parameter that can be a button item or a container. It requires that you look at the source to understand it, instead of being (somewhat) obvious from the interface.

Reloading a non-table view (iOS)

I'm working on a painting app, and I want to user to be able to switch through paintings that are saved as PNGs to the documents directory. I've made an IBAction that does this:
-(IBAction)switchPage:(id)sender{
if ([sender tag] ==1)
{
currentImage=currentImage-1;
}
else
{
currentImage++;
}
[[NSUserDefaults standardUserDefaults] setInteger:currentImage forKey:#"currentDoc"];
[self.view setNeedsDisplay];
}
Just to let you know, I have 2 buttons, a back and forward, and they're connected to the same IBAction that's pasted above. The back button has a tag of 1, and the forward button doesn't have a tag. currentImage is an int that is used to set an image's name, so if currentImage =1, then it would set the image to image1. But that's not what I'm having trouble with, I'm having trouble "reloading" the view. I'm using setNeedsDisplay, but it's not working, and I know that reloadData is only for UITables, so what else could I use?
I've been searching for an answer, but none of the existing questions about this don't have a clear answer, or are under different circumstances.
Thanks for your time and/or dealing with a stupid question, as I'm new to Xcode.
-Karl
I see no view code in your post.
Assuming you have a UIImageView that is displaying the image you would just do:
myImageView.image = whateverUIImage;

Prevent choosing of certain objects in NSMutableArray?

I am kind of in a pickle here since I have tried many things to accomplish this and have failed.
What I want to do: I have a NSString in my app which is the answer to a question to a round in a quiz. My answers are stored in a NSMutableArray. These answers are chosen at random. I have 4 buttons in my app that are options (possible answers) for the round. I don't want to have multiple answers!
What I have tried:
I have tried deleting the answer after storing it in one of the buttons so that it couldn't be chosen again but it ended up causing a crash since I was trying to delete a object in the array while using fast enumeration (for loop).
I have tried just detecting if the button title was equal to to the answer after setting the correct answer to a specific button but that didn't work for some odd reason (no crash). There will still multiple buttons with the same answer
What I need help with: How should I stop the answer from being in multiple buttons so that the quiz isn't showing the obvious answer?
What should I do instead?
Thanks!
Edit1: So I pretty much made a NSArray of my four UIButtons.
I put the answer into a random UIButton like this:
NSInteger chosen = (arc4random() % 4);
UIButton *randButton = (UIButton *)[buttonArray objectAtIndex:chosen];
[randButton setTitle:imageName forState:UIControlStateNormal];
Then I title the other buttons like so, my buttons that do not have the answer have a title of nothing so I do this:
- (void)titleButtons {
for (UIButton *buttons in buttonArray) {
if ([[buttons titleForState:UIControlStateNormal] == nil]) {
UIButton *button = buttons;
NSString *superheroString = (NSString*)[superheroArray objectAtIndex:(arc4random() % [superheroArray count])];
[button setTitle:superheroString forState:UIControlStateNormal];
[self checkTitles:button];
}
}
Then the checkTitle method looks like this, this is the method where I try to make sure where 2 buttons do not have the same answer as the imageName (answer):
- (void)checkTitles:(UIButton*)button {
if ([[button titleForState:UIControlStateNormal] isEqualToString:imageName]) {
//Duplicate Answer so re-title button
NSString *newTitle = [superheroArray objectAtIndex:(arc4random() % [superheroArray count])];
[button setTitle:newTitle forState:UIControlStateNormal];
//Call same method again to see if new title is still same answer as pic to avoid same answers
[self checkTitles:button];
}
}
}
If you have an NSArray with all your answers, and you want 1 correct answer and 3 different wrong answers, you can do the following:
Decide in which button do you want the right answer. (randomize it)
Get a random answer from your array and store the index of that answer in a temporary array.
Get another random answer and make sure you are not picking an answer with the same index as the one you have in your temp array, (and again, store the new index in the temporary array)
Don't compare strings using ==. This compares pointer addresses. Use isEqualToString: instead:
if (stringA == stringB)
Usually won't work (will for some constant strings)
if ([stringA isEqualToString:stringB])
Will always work.

Resources