EXC_BAD_ACCESS when trying to get selectedSegmentIndex of UISegmentedControl - iphone

I'm programmatically adding a UISegmentedControl to my UINavigationBar as follows
UISegmentedControl *toggleSwitch = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects: #"Bar", #"Scatter", nil]];
toggleSwitch.segmentedControlStyle = UISegmentedControlStyleBar;
[toggleSwitch addTarget:self action:#selector(toggleSwitched:) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:toggleSwitch];
self.navigationItem.rightBarButtonItem = buttonItem;
[toggleSwitch release];
[buttonItem release];
I then have a method, toggleSwitched to handle the events:
-(void) toggleSwitched:(id) sender {
if([sender isKindOfClass:[UISegmentedControl class]]) {
NSLog(#"%#",((UISegmentedControl*)sender).selectedSegmentIndex );
}
}
From my reading of the Apple Documentation, this is the proper way to set up the event handling for the touch event.
HOWEVER, when I run the program (in the simulator) and tap the segments, the following things happen:
If it's the first segment (index 0), 'null' is logged to the console
If it's the second segment (index 1), the program crashes with EXC_BAD_ACCESS
When I look in the variables window of the debugger, sender is of type UISegmentedControl and the _selectedSegment property appears to be correct (0 or 1).
What am I improperly accessing and how can I set up my delegate function to switch between the two values of the UISegmentedController?

Your NSLog is wrong. selectedSegmentIndex returns NSUInteger, so you need to use #"%d" instead of #"%#". %# is for objects, so the system is trying to use the number as a memory address that points to a object, and generating an error for you.

Related

How to check whether the UISegmentedControl is selected or not by user?

I got an app in which I allocate 5 UISegmentedControl dynamically into the view. And got a Done button at the end. My condition that to proceed into next step (when done button is pressed), all the UISegmentControls "should be selected by user".
The default selection in segmentcontrol is none.
How to check whether all the UISegmentedControls in my view is selected by the user before action on the done button is executed?
Right from the apple document, this should answer your question:
#property(nonatomic) NSInteger selectedSegmentIndex
Discussion
The default value is UISegmentedControlNoSegment (no segment selected) until the user touches a segment.
Hope you can use that to check whether the value is user selected or not, to prevent going to next page.
NSLog(#"%i", self.segment.selectedSegmentIndex);
this results -1 if no segment is selected.
add target and action to your segmentControls for UIControlEventValueChanged.
From the selector you gave in action, check which segmentControl was changed, and set it's corresponding flag (ex: array of string which are #"0" for not selected and #"1" once selected).
At any time check which flags are not set, the corresponding segmentControls were never selected.
This is how u create an UISegmentedControl
NSArray *itemArray = [NSArray arrayWithObjects: #"Title1", #"Title2", #"Title3", #"Title4",nil];
segmentedControl = [[UISegmentedControl alloc] initWithItems:itemArray];
segmentedControl.frame = CGRectMake(0, 0, 310, 35);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.selectedSegmentIndex = 0;
[segmentedControl addTarget:self action:#selector(pickOne:) forControlEvents:UIControlEventValueChanged];
segmentedControl.tintColor=[UIColor grayColor];
Then to find which segment was selected,
NSString *category =[segmentedControl titleForSegmentAtIndex: [segmentedControl selectedSegmentIndex]];
if(category==#"Title1"){
//Do something here..
}
Hope this helps.... Happy Coding

IBAction used with Dynamically added UIbuttons

Good Evening all!
I have some UIButtons added dynamically into my view and of course I have an IBAction which handles button events.
The problem is: How can I detect which button is pressed if the only thing I know is the (id)sender and the array of buttons?
Buttons are never the same, every button has a different behavior. When I want to use static buttons and connect them through the IB I use something like this :
-(IBAction)doSomething:(id)sender
{
if(sender == button1)
dosomething;
if(sender == button2)
dosomething else;
if(sender == button3)
dosomething3;
}
In my case this does not work because there is no button1, button2, button3 but a MutableArray of buttons which have the same name as they were allocated with it. Button!
I tried using the way above but with no success and i tried also getting the tag of a button but I have nothing to compare it to!
I would really appreciate your help.
sincerely
L_Sonic
PS Dynamicaly means that i am creating the buttons in random time during run time like this
-(void)configActionSheetView
{
buttonView = [[UIView alloc]initWithFrame:CGRectMake(0.0,460, 60, 480)];
[buttonView setBackgroundColor:[UIColor blackColor]];
[buttonView setAlpha:0.6];
for (int i = 0 ;i<[buffButtons count];i++)
{
UIButton *customButton = [buffButtons objectAtIndex:i];
customButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
//UILabel *customLabel = [[UILabel alloc]init];
//[customButton setTag:(i)+11];
[customButton addTarget:self action:#selector(activateBuffEffect:) forControlEvents:UIControlEventTouchUpInside];
[customButton setAlpha:1.0];
customButton.frame = CGRectMake(8.0, 5+(50*i), 44.0, 44.0);
[customButton setTitle:nil forState:UIControlStateNormal];
buttonView.frame = CGRectMake(0, 460, 60, 50+(44*(i+1)));
[buttonView addSubview:customButton];
}
}
this is inside a functions and gets called during run time. the buffButtons is a mutableArray with buttons that gets populated during runtime.
i need a solution like this i cannot get a different eventhandling method for everybutton.
When you was "added dynamically" I assume you mean that they are created from some piece of code. Since all buttons to different things and you know what a certain button should do, why don't you add different actions to different buttons?
UIButton *myCreatedButton = [[UIButton alloc] init];
[myCreatedButton addTarget:self
action:#selector(doSomething:)
forControlEvents:UIControlEventTouchUpInside];
UIButton *myOtherCreatedButton = [[UIButton alloc] init];
[myOtherCreatedButton addTarget:self
action:#selector(doSomethingElse:)
forControlEvents:UIControlEventTouchUpInside];
In the above code the target (set to self) is the class where the method you want to run is found, the action is the method that you want to run and the controlEvent is what should cause the method to run.
If you did it like this you would split the code in different methods like these (you do not need to specify them in the header):
-(void)doSomething:(id)sender {
// do somthing here ...
}
-(void)doSomethingElse:(id)sender {
// do somthing else here ...
}
This way you don't need to know what button was pressed since the correct code would get called anyway. Besides it makes it cleaner if you need to change the code for some of the buttons.
Found it!
-(IBAction)buttonTapped:(id)sender {
UIButton *btn = (UIButton *)sender;
NSLog(#"tapped: %#", btn.titleLabel.text);
[self anotherIBAction:sender];
}
now i can get the tag from the btn :D
thnk you!
Why not add a tag the button and then get the tag number from (id)sender in the selector function?

How to customize MPVolumeView?

I have tried many methods to implement a regular UISlider and control the device volume, but it's all Native-C functions which results in many untraceable bugs.
I tried the MPVolumeView it works like charm, it even controls the device volume even after you close the app, just like the iPod app.
My question is, is there anyway to customize the MPVolumeView with specific colors and images, just like UISlider?
NOTE: I want a legal method without using private undocumented APIs.
UPDATE
As per #Alexsander Akers answer, since the sub views are hidden in MPVolumeView I had to cycle through subviews, get the UISlider and customize it, here is the code.
IBOutlet UISlider *volumeSlider; //defined in <class.h> and connected to a UISlider in Interface Builder
-(void) viewDidLoad {
....
[self setCustomSlider];
....
}
-(void) setCustomSlider{
MPVolumeView *volumeView = [[[MPVolumeView alloc] initWithFrame:[volumeSlider frame]] autorelease];
NSArray *tempArray = volumeView.subviews;
for (id current in tempArray){
if ([current isKindOfClass:[UISlider class]]){
UISlider *tempSlider = (UISlider *) current;
UIImage *img = [UIImage imageNamed:#"trackImage.png"];
img = [img stretchableImageWithLeftCapWidth:5.0 topCapHeight:0];
[tempSlider setMinimumTrackImage:img forState:UIControlStateNormal];
[tempSlider setThumbImage:[UIImage imageNamed:#"thumbImage.png"] forState:UIControlStateNormal];
}
}
[volumeSlider removeFromSuperview];
[self.view addSubview:volumeView];
}
You could try cycling through its subviews and look for a UISlider subclass?
Since iOS 5.0 you can use UIAppearance on a UISlider, even when part of MPVolumeView.
Anywhere in your codebase:
[[UISlider appearanceWhenContainedIn:[MPVolumeView class], nil] setMinimumTrackImage:[[UIImage imageNamed:#"nowplaying_bar_full.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(5, 25, 5, 25)] forState:UIControlStateNormal];
[[UISlider appearanceWhenContainedIn:[MPVolumeView class], nil] setMaximumTrackImage:[[UIImage imageNamed:#"nowplaying_bar_empty.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(5, 25, 5, 25)] forState:UIControlStateNormal];
[[UISlider appearanceWhenContainedIn:[MPVolumeView class], nil] setThumbImage:[UIImage imageNamed:#"nowplaying_player_nob.png"] forState:UIControlStateNormal];
Here a list of some of the other classes that can be implemented using UIAppearance:
https://gist.github.com/mattt/5135521
There are now ways to accomplish this, simply use:
– setMaximumVolumeSliderImage:forState:
– setMinimumVolumeSliderImage:forState:
– setVolumeThumbImage:forState:
Which are slightly different method names than the ones for the vanilla UISlider.
This prevents you from having to cycle through the views and potentially have something break in the future as Apple changes things.
Answer in Swift:
func customSlider() {
let temp = mpVolView.subviews
for current in temp {
if current.isKind(of: UISlider.self) {
let tempSlider = current as! UISlider
tempSlider.minimumTrackTintColor = .yellow
tempSlider.maximumTrackTintColor = .blue
}
}
}
Result:
Try using a Notification, but it looks like Apple is denying them.
[EDIT]
Try this.

Identifying a UISegmentedControl Inside a UITableViewCell

I am trying to work with a segmented control inside a a tableview, then when a user selects an item, I'd like to show a spinner, while some info posts to a webservice.
The problem that I am having is: How do I add a delegate and access the referenced segmented Control, so I can set it's alpha or visibility to NO? Also, what's the best practice for this, I know there's tags, but not sure how they work in this type of situation.
NSArray * segmentItems= [NSArray arrayWithObjects: #"one", #"two", #"three", #"four", #"five", nil];
UISegmentedControl *segmentedControl= [[[UISegmentedControl alloc] initWithItems: segmentItems] retain];
segmentedControl.segmentedControlStyle= UISegmentedControlStyleBar;
segmentedControl.selectedSegmentIndex= -1;
[segmentedControl addTarget: self action: #selector(onSegmentedControlChanged:) forControlEvents: UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(2, 0, 300, 30);
segmentedControl.tintColor= [UIColor grayColor];
Here's my delegate
- (IBAction)onSegmentedControlChanged:(id)sender
{
int clickedSegment= [sender selectedSegment];
}
How do I access the UISegmentedControl from the sender so I can set the visibility Off? I can always set the my object that populates my segmentedControl by extending it, I Just need to figure out how to get a reference to the the cell and the SegmentedControl?
When you create/return the cell with the segmented control, set the delegate object before returning the cell to the table.
Edit:
Sorry, misread the question. The sender passed to the delegate methods will be the actual UISegmentedControl instance you need to identify. Inside the delegate method, cast the generic sender to a UISegmentedControl` then set its attributes as needed.
The selected table row is passed to the tableview selection methods.

Views load slowly when switching between views on 3G iPhones - how to change my style?

In my iPhone app, views will often load slowly when transitioning, like if a user clicks a button on the Tab Bar Controller. This happens more if the phone is low on memory. It doesn't really come up on 3GS phones, but it's a big problem on 3G phones.
I suspect that I'm not following some best practices for creating UIViewControllers. I think I might be doing too much in the init functions, not using the viewDidLoad function, or something. It seems to affect all my views, so I think it's a problem with my style in general, not some particular snippet.
Can anyone tell me what i might be doing wrong? Here is some sample code, from a UIViewController subclass:
EDIT: In response to the question: "where is this being called?"
This function gets called in this case when the user clicks a marker on the map:
if(marker.label.tag == SavedBookmarkTag) {
SavedDetailScreen *savedBookmark = [[[SavedDetailScreen alloc] initBookmarkView:
[(NSDictionary *)marker.data objectForKey:#"bookmark"]]autorelease];
[savedBookmark showMap];
[self.navBar pushViewControllerWithBackBar:savedBookmark];
return;
}
END EDIT
-(id)initBookmarkView: (Bookmark *)bm {
self = [self initView];
self.bookmark = bm;
primaryLabel.text = [bm title];
secondaryLabel.text = [self getLineWithLat:[bm lat] AndLon:[bm lon] AndDate:[bm timeCreated]];
return self;
}
- (id)initView {
self = [super init];
self.isWaypoint = NO;
UIImageView *bg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"238-beveled-background.png"]];
bg.frame = CGRectMake(0, 0, 320, 376);
[self.view addSubview:bg];
bg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"small-label.png"]];
[self.view addSubview:bg];
[bg release];
self.primaryLabel = [[UILabel alloc]init];
primaryLabel.font = TITLE_FONT;
primaryLabel.backgroundColor = [UIColor clearColor];
primaryLabel.textColor = LIGHT_BLUE;
self.secondaryLabel = [[UILabel alloc]init];
secondaryLabel.font = TEXT_FONT;
secondaryLabel.backgroundColor = [UIColor clearColor];
secondaryLabel.textColor = LIGHT_BLUE;
secondaryLabel.lineBreakMode = UILineBreakModeClip;
self.thirdLabel = [[UILabel alloc]init];
thirdLabel.font = TEXT_FONT;
thirdLabel.backgroundColor = [UIColor clearColor];
thirdLabel.textColor = LIGHT_BLUE;
thirdLabel.lineBreakMode = UILineBreakModeCharacterWrap;
[self.view addSubview:primaryLabel];
[self.view addSubview:secondaryLabel];
[self.view addSubview:thirdLabel];
self.loadingBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"stats-box.png"]];
loadingBackground.frame = CGRectMake(0, 115, loadingBackground.frame.size.width, loadingBackground.frame.size.height);
[self.view addSubview:loadingBackground];
[self.view sendSubviewToBack:loadingBackground];
AnimatedGif *animatedGif = [[[AnimatedGif alloc] init] autorelease];
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"35" ofType:#"gif"]];
[animatedGif decodeGIF: data];
UIImageView *loadingImage = [animatedGif getAnimation];
loadingImage.frame = CGRectMake(150,150,loadingImage.frame.size.width,loadingImage.frame.size.height);
[loadingImage startAnimating];
[loadingBackground addSubview:loadingImage];
[loadingImage release];
[self layoutSubviews];
return self;
}
- (void) layoutSubviews {
self.view.frame = CGRectMake(0,0,320,372);
primaryLabel.frame = CGRectMake(30, 30, 260, 18);
secondaryLabel.frame = CGRectMake(30 ,52, 260, 16);
thirdLabel.frame = CGRectMake(30, 72, 260, 16);
}
The slowness that you're seeing can probably be attributed to the fact that constructing objects and reading data from the flash are both expensive processes. Look for opportunities to reuse existing objects rather than constructing them multiple times, and consider deferring especially expensive operations until after the view gets displayed.
In this case, I would start with a couple changes:
Make savedBookmark a member variable so that you can construct it once and reuse it. Replace your initBookmarkView: method with a setBookmarkView: method that you can call after this member variable is constructed to reconfigure your labels for the specific bookmark being displayed.
Take the subview creation code out of initView and put it in loadView. This is the most appropriate place to construct your own view hierarchy programmatically. UIViewController implements lazy loading on its view property to defer construction as long as possible. The view property is nil until the first time it's requested. At that point UIViewController calls loadView to set the property. The default implementation loads the view from a nib file if one is defined. Otherwise it just constructs an empty UIView and makes that the main view. Note that you'll have to construct the container view and set the view property yourself.
In other apps you may get some improvement by moving some initialization code into viewDidLoad:, which gets called after the view property is loaded, whether programmatically or from a nib. If you ever have an especially slow operation like loading images from a remote URL, you might want to start loading the data asynchronously in viewDidLoad:, and then update your subviews once the data finishes loading. In some cases you may also want to defer some code until viewDidAppear:. Just be aware that this method gets called every time the view appears, unlike loadView and viewDidLoad:, which only get called once.