Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matching MPVolumeView and UISlider vertical positions in a UIToolBar

I have a UIToolBar that is intended to contain sliders for controlling volume and brightness. I use a MPVolumeView slider for the volume, and an ordinary UISlider for the brightness. While the sliders themselves work fine, their vertical positions are mismatched:

Mismatched UISlider and MPVolumeView

How do I get them to be on the same height?

My code:

- (void) createToolbar{

toolBar = [[UIToolbar alloc] init];
toolBar.frame = CGRectMake(0, 0, self.view.frame.size.width, 44);

UISegmentedControl *modeSelector = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Play", @"Rec", nil]];
[modeSelector setSegmentedControlStyle:UISegmentedControlStyleBar];
[modeSelector addTarget:self action:@selector(changePlayMode) forControlEvents:UIControlEventValueChanged];
modeSelector.selectedSegmentIndex = 0;
UIBarButtonItem *modeSelectorAsToolbarItem = [[UIBarButtonItem alloc] initWithCustomView:modeSelector];

brightnessSlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 150, 30)];
brightnessSlider.minimumValue = 0;
brightnessSlider.maximumValue = 1;
brightnessSlider.value = [[UIScreen mainScreen] brightness];
brightnessSlider.continuous = YES;
[brightnessSlider addTarget:self action:@selector(adjustBrightness:) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *brightSliderAsToolbarItem = [[UIBarButtonItem alloc] initWithCustomView:brightnessSlider];

MPVolumeView *volView = [[MPVolumeView alloc] initWithFrame:CGRectMake(0, 0, 150, 30)];
volView.showsRouteButton = NO;
UIBarButtonItem *volSliderAsToolbarItem = [[UIBarButtonItem alloc] initWithCustomView:volView];

UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *toc = [[UIBarButtonItem alloc] initWithTitle:@"Contents" style:UIBarButtonItemStyleBordered target:self action:@selector(goToToC)];
[toolBar setItems:[NSArray arrayWithObjects:modeSelectorAsToolbarItem, flexibleSpace, brightSliderAsToolbarItem, volSliderAsToolbarItem, flexibleSpace, toc, nil] animated:NO];

toolBar.autoresizingMask |= UIViewAutoresizingFlexibleWidth;

[[self view] addSubview:toolBar];

}

(Changing the CGRectMake coordinates doesn't seem to do anything.)

A comment in the question "Custom MPVolumeView Thumb Image not vertically centered since iOS 5.1" seemed to suggest using the "Fixing thumb alignment" trick explained here, but implementing that code didn't seem to do anything as far as I could tell, and I'm not sure whether it was talking about the same problem.

like image 773
Kaj_Sotala Avatar asked May 28 '13 08:05

Kaj_Sotala


3 Answers

If you create a trivial subclass of MPVolumeView and override volumeSliderRectForBounds:, you can define your own alignment for the slider rect. I like to return the whole bounds, which centers the slider in the MPVolumeView's frame

@interface KMVolumeView : MPVolumeView

@end

@implementation KMVolumeView

- (CGRect)volumeSliderRectForBounds:(CGRect)bounds
{
    return bounds;
}

@end

Just use your subclass in code or in interface builder and you can then reliably position the volume view.

like image 94
kmikael Avatar answered Oct 15 '22 09:10

kmikael


I came up with something, that extends kmikael's answer:

@interface SystemVolumeView : MPVolumeView

@end

@implementation SystemVolumeView

- (CGRect)volumeSliderRectForBounds:(CGRect)bounds {
    CGRect newBounds=[super volumeSliderRectForBounds:bounds];

    newBounds.origin.y=bounds.origin.y;
    newBounds.size.height=bounds.size.height;

    return newBounds;
}

- (CGRect) routeButtonRectForBounds:(CGRect)bounds {
    CGRect newBounds=[super routeButtonRectForBounds:bounds];

    newBounds.origin.y=bounds.origin.y;
    newBounds.size.height=bounds.size.height;

    return newBounds;
}

@end

This implementation differs in that it still uses the default horizontal values but overrides the vertical ones in order to keep the MPVolumeView centered vertically in its container. It also overrides -routeButtonRectForBounds: so that the airplay/route button gets centered as well.

I have to wonder why the default implementation has a wonky vertical position.

like image 43
hyperspasm Avatar answered Oct 15 '22 08:10

hyperspasm


SWIFT Version: come form hyperspasm's answer:

import Foundation

class SystemVolumeView: MPVolumeView {
    override func volumeSliderRect(forBounds bounds: CGRect) -> CGRect {
        var newBounds = super.volumeSliderRect(forBounds: bounds)
        newBounds.origin.y = bounds.origin.y
        newBounds.size.height = bounds.size.height
        return newBounds
    }
    override func routeButtonRect(forBounds bounds: CGRect) -> CGRect {
        var newBounds = super.routeButtonRect(forBounds: bounds)
        newBounds.origin.y = bounds.origin.y
        newBounds.size.height = bounds.size.height
        return newBounds
    }
}
like image 6
Jerome Avatar answered Oct 15 '22 08:10

Jerome