Matching MPVolumeView and UISlider vertical positions in a UIToolBar
Asked Answered
B

4

6

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.

Bath answered 28/5, 2013 at 8:53 Comment(0)
C
21

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.

Championship answered 19/11, 2013 at 11:1 Comment(2)
I would just like to point out that Apple now says "You can not subclass the MPVolumeView class." developer.apple.com/documentation/mediaplayer/mpvolumeviewThyratron
@EricArmstrong that's probably just an outdated note, given the MPVolumeView was introduced in iOS 2.0, and those customization APIs were introduced in iOS 6.0.Turnedon
U
16

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.

Urtication answered 26/12, 2013 at 2:39 Comment(1)
Great Hack, still working on iOS8 ! In don't understand the logic in Apple implementation ... If some have an idea, I would like to know it !Erotic
W
6

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
    }
}
Wainwright answered 16/11, 2016 at 2:18 Comment(0)
B
0

I ended up just using UISlider for both, using the instructions provided in the "Get System Volume" and "Change System Volume" answers to make a volume slider instead of using MPVolumeView.

Bath answered 1/6, 2013 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.