How to customize MPVolumeView? - iphone

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.

Related

EXC_BAD_ACCESS when trying to get selectedSegmentIndex of UISegmentedControl

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.

add custom switch to iphone view

I am using the following code to create a custom switch:
UISegmentedControl* switchView=[[UISegmentedControl alloc]
initWithItems:[[NSMutableArray alloc] initWithObjects:#"On",#"Off",nil]];
[switchView setFrame:CGRectMake(20,365,140,28)];
switchView.selectedSegmentIndex=0;
switchView.segmentedControlStyle=UISegmentedControlStyleBar;
[switchView setImage:[UIImage imageNamed:#"onSelected.png"] forSegmentAtIndex:0];
[switchView setImage:[UIImage imageNamed:#"off.png"] forSegmentAtIndex:1];
[switchView addTarget:self action:#selector(checkOnOffState:) forControlEvents:UIControlEventValueChanged];
I would now like to add this control to one of my screens (I believe the correct term is ViewController?). How would I do this in Xcode 4.3?
I looked at a few other posts and was not able to find anything that works. For example, How do I add a custom view to iPhone app's UI? suggested something along the lines of
[[myVC view] addSubview:myView];
and How to customize UISwitch button in iphone? suggested
self.navigationItem.titleView=switchView;
If possible, could you also explain why these approaches did not work? Thanks.
You need to create a class for your switch which is subclass of your UISegmentControl ,
Else you can add this code in your vc directly as well.
Add this code in its initWithFrame method
and then use it in your VC
-(void)viewDidLoad{
urCustomSwitch = [urCustomSwitch alloc] initWithFrame:CGRectMake(20,365,140,28)];
[self.view addSubview:urCustomSwitch];
}
This shall serve your purpose if you are to create custom reusable class.
If you add this switch control i.e. UISegmentedControl in InterfaceBuilder (.xib file)
This task will be much easier for you.

UISegmentedControl and adding targets

I'm sure this is an easy one for someone out there. I have a UISegmentedControl which I am using as a button (so as not to have to use the nasty default button) and I am having trouble getting the target to work....code as follows
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//read.buttonType = UIBarStyleBlackOpaque;
UISegmentedControl* read = [[[UISegmentedControl alloc] initWithFrame:CGRectMake(5, 50, 310, 54)] autorelease];
[read insertSegmentWithTitle:#"Read" atIndex:0 animated:NO];
read.tintColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.9 alpha:1];
read.segmentedControlStyle = UISegmentedControlStyleBar;
[read addTarget:self action:#selector(changeFilter:sender:) forControlEvents:UIControlEventTouchUpInside];
[read setTag:1];
[self.view addSubview:read];
}
and then
-(void)changeFilter:(id)sender{
}
for some reason, clicking on the UISegmentedControl does not fire the target method.
As an addendum, is there a simpler way to make nice looking UIButtons? I don't have access to Photoshop at work (although I do have gimp installed), so a way which doesn't involve image making would be good. I can't believe that apple didn't supply nice looking UIButtons in the UI, it seems such a fundamental thing to need?
Anyway, thanks for the help mis amigos.
thanks for the answers...I've tried the fix but it still doesn't fire
I now have
[read addTarget:self action:#selector(changeFilter:) forControlEvents:UIControlEventTouchUpInside];
then
#interface
-(void)changeFilter:(id)sender;
and
#implementation
-(void)changeFilter:(id)sender{}
Note that the method is in the same class as the UISegmentedControl. Maybe I should just try using the Glass button API which was suggested, but my boss hates me using third party libraries if there is a way of avoiding it!
The selector is wrong. It should be, action:#selector(changeFilter:), And change the event to forControlEvents:UIControlEventValueChanged, as UISegmentedControl doesn't send any actions for UIControlEventTouchUpInside event.
.h file u forgot to put this
-(void)changeFilter:(id)sender;
then
change this
[read addTarget:self action:#selector(changeFilter:sender:) forControlEvents:UIControlEventTouchUpInside];
To
[read addTarget:self action:#selector(changeFilter:) forControlEvents:UIControlEventValueChanged];
The action will automatically send the sender so just try #selector(changeFilter:).
As for your second question, ios5 will help you out there... but I can't say much about that yet. For now, you can use some uibutton classes that people have made to easily make some good looking buttons. Here's one:
http://hboon.com/glass-buttons-in-iphone-apps-without-using-image-files/

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 change uibutton title at runtime in objective c?

I know this question is asked many a times,and i am also implementing the same funda for chanding the title of the uibutton i guess.
Let me clarify my problem first. I have one uibutton named btnType, on clicking of what one picker pops up and after selecting one value,i am hitting done button to hide the picker and at the same time i am changing the the title of the uibutton with code
[btnType setTitle:btnTitle forState:UIControlEventTouchUpInside];
[btnType setTitleColor:[UIColor redColor] forState:UIControlEventAllEvents];
But with my surpriaze,it is not changed and application crashes with signal EXC_BAD_ACCESS. I am not getting where i am making mistake.I have allocated memory to the btnType at viewdidLoad. Also I am using
-(IBAction)pressAddType
{
toolBar.hidden = FALSE;
dateTypePicker.hidden = FALSE;
}
event on pressing the button to open the picker. Also i would like to mention that i have made connection with IB with event TouchUpInside for pressAddType.
Any guesses? I will be grateful if you could help me.
Thanks.
UPDATE:
#interface AddSettingPage : UIViewController<UITextFieldDelegate>
{
IBOutlet UIButton *btnType;
NSString *btnTitle;
}
#property (nonatomic, retain) IBOutlet UIButton *btnType;
-(IBAction)pressAddType;//:(id)sender;
Also
#synthesize btnType,btnTitle;
try
[yourButton setTitle:#"your title" forState:UIControlStateNormal];
[yourButton setTitle:#"your title" forState:UIControlStateSelected];
[yourButton setTitle:#"your title" forState:UIControlStateHighlighted];
when the picker is dismissed, the button (which was the control that hold focus) will be in the selected state (or the highlighted .. check it out).
and stop using UIControlEventTouchUpInside in the forState: parameter. it is not a state, it is an event. you are passing an event identifier instead of a state identifier
The state you pass in setTitle should be something like UIControlStateNormal:
[b setTitle:#"" forState:UIControlStateNormal];
Instead of
- (IBAction) pressAddType;
declare
- (IBAction) pressAddType:(id)sender; //or (UIButton *)sender
and define it like:
-(IBAction)pressAddType:(id)sender
{
toolBar.hidden = FALSE;
dateTypePicker.hidden = FALSE;
[(UIButton *)sender setTitle:btnTitle forState:UIControlEventTouchUpInside];
[(UIButton *)sender setTitleColor:[UIColor redColor] forState:UIControlEventAllEvents];
}
As you can see, you don't need to have your button as an ivar because it is passed as a parameter of the method when pressed.
This answer why not button title change only,EXC_BAD_ACCESS error only you getting when an object you trying to access those object is not in memory of stack or that object has null value. So my advice is please check your object (btnTitle) is in memory or not?