MPVolumeView redraws incorrectly in iOS 7

So, I have an iOS7 application in which I use MPVolumeView so that the user can control the volume level. I have a route button hidden on this particular MPVolumeView and use another MPVolumeView when the slider is disabled as an AirPlay icon.

My application supports orientation in both portrait and landscape orientation, and the volume slider is different in two different modes.

If the view is first initialized in landscape mode, then MPVolumeView will change correctly.

However, when the view is initialized in portrait mode, and then I turn to landscape mode, EVERYTHING in the application changes / moves, except that the MPVolumeView only moves and it does not get shorter as it should.

I use custom images in MPVolumeView , and if I delete custom images for the track, this problem will go away.

Here is the code used to initialize MPVolumeView

  self.volumeView = [[MPVolumeView alloc] initWithFrame:CGRectZero]; self.volumeView.showsRouteButton = NO; self.volumeView.layer.borderColor = [[UIColor redColor] CGColor]; self.volumeView.layer.borderWidth = 1.0f; if (AT_LEAST_IOS7) { // TODO: BUGBUG iOS7 doesn't redraw this MPVolumeView correctly when it frame changes (ie we rotate to the portrait view). // These images simply make the bar a little thicker than the standard thickness (to match the iOS7 music app) but it not redrawing // correctly so we are going to have to live with a slightly thinner bar. [self.volumeView setMaximumVolumeSliderImage:[UIImage imageNamed:@"volume_bar_max"] forState:UIControlStateNormal]; [self.volumeView setMinimumVolumeSliderImage:[UIImage imageNamed:@"volume_bar_min"] forState:UIControlStateNormal]; [self.volumeView setVolumeThumbImage:[UIImage imageNamed:@"volume_scrubber"] forState:UIControlStateNormal]; } [self addSubview:self.volumeView]; 

And in layoutSubviews, I move / scale it:

 self.volumeView.frame = CGRectIntegral(CGRectMake(controlsLeft + kEdgeToSliderSideWidth, volumeTop, controlsWidth - (2 * kEdgeToSliderSideWidth), volumeSize.height)); 

Here's what it looks like when a view starts in portrait mode: (total width of 640 pixels)

Portrait mode

And when it rotates to Landscape, it looks like this: (total width 568 pixels)

Landscape mode

Does anyone have any ideas?

+8
ios ios7 uiview mpvolumeview
source share
4 answers

I have the same problem. My current workaround is to destroy and re-add the slider after rotating the screen:

 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { //remove the old view for (UIView *subview in self.volumeViewContainer.subviews) [subview removeFromSuperview]; //recreate it UIImage *slider = [[UIImage imageNamed:@"slider"] resizableImageWithCapInsets:UIEdgeInsetsMake(0.0, 15.0, 0.0, 15.0)]; UIImage *knobImage = [UIImage imageNamed:@"slider_knob"]; MPVolumeView *volumeView = [[[MPVolumeView alloc] initWithFrame:self.volumeViewContainer.bounds] autorelease]; [volumeView setMinimumVolumeSliderImage:slider forState:UIControlStateNormal]; [volumeView setMaximumVolumeSliderImage:slider forState:UIControlStateNormal]; [volumeView setVolumeThumbImage:knobImage forState:UIControlStateNormal]; [self.volumeViewContainer addSubview:volumeView]; } 

The implementation of this problem is still related to the fact that the slider is still displayed with errors DURING rotation. This is why I rotate the slider to the image and change the slider with the specified image before the rotation occurs:

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { //render to UIImage UIGraphicsBeginImageContextWithOptions(self.volumeViewContainer.frame.size, NO, 0.0); [self.volumeViewContainer.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); //remove old slider for (UIView *subview in self.volumeViewContainer.subviews) [subview removeFromSuperview]; //add UIImageView which resizes nicely with the container view UIImageView *imageView = [[[UIImageView alloc] initWithFrame:self.volumeViewContainer.bounds] autorelease]; imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; imageView.image = img; [self.volumeViewContainer addSubview:imageView]; } 

I know this is not a real solution, but I think it is better than nothing and I hope this helps you.

+4
source share

After a lot of experimentation, it looks like MPVolumeView is very buggy in iOS 7.0.x.

I had a problem when custom images for the min / max, large volume and large volume sliders sometimes caused the slider to expand to close the route button.

When I changed the order in which they are called, he fixed the problem. The trick is to call setVolumeThumbImage and setRouteButtonImage first, then set MinimumVolumeSliderImage and setMaximumVolumeSliderImage.

This entailed overlapping the track above the route button.

HOWEVER, this caused a new problem in which maxSliderImage began to overlap volumeThumbImage!

The final solution was to put the thumb layer forward as follows, in a subclass of MPVolumeView:

 - (void)_initialize { UIImage *scrubber = [UIImage imageNamed:@"volume_scrubber_grip"]; scrubberSize = scrubber.size; [self setVolumeThumbImage:scrubber forState:UIControlStateNormal]; [self setRouteButtonImage:[UIImage imageNamed:@"airplay_button"] forState:UIControlStateNormal]; [self setMinimumVolumeSliderImage:[UIImage imageNamed:@"min_slider"] forState:UIControlStateNormal]; [self setMaximumVolumeSliderImage:[UIImage imageNamed:@"max_slider"] forState:UIControlStateNormal]; } - (void)layoutSubviews { [super layoutSubviews]; [self.subviews enumerateObjectsUsingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) { if([obj isKindOfClass:[UISlider class]]) { UISlider *slider = (UISlider *)obj; [slider.subviews enumerateObjectsUsingBlock:^(UIView *obj2, NSUInteger idx, BOOL *stop2) { if(CGSizeEqualToSize(obj2.frame.size, scrubberSize)) { [obj bringSubviewToFront:obj2]; *stop2 = YES; } }]; *stop = YES; } }]; } 
+2
source share

I tested another solution that works great. I embed an MPVolumeView in a simple UIView (which can be created in a storyboard). Then I observe the key values ​​at the "borders" of the UIView property. When this property changes, I update the MPVolumeView frame to the new borders of the UIView .

This solution handles both autorotation and autorun.

 import UIKit import MediaPlayerclass ViewController: UIViewController { @IBOutlet weak var volumeViewContainer: UIView! override func viewDidLoad() { super.viewDidLoad() volumeViewContainer.backgroundColor = UIColor.grayColor() var volumeView = MPVolumeView(frame: volumeViewContainer.bounds) volumeViewContainer.addSubview(volumeView) volumeViewContainer.addObserver(self, forKeyPath: "bounds", options: nil, context: nil) } override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) { if let subview = volumeViewContainer.subviews.first as? UIView { subview.frame = volumeViewContainer.bounds } } } 
+1
source share
 @IBOutlet var volumeViewHolder: UIView! override func viewDidLoad() { super.viewDidLoad() volumeViewHolder.backgroundColor = UIColor.clearColor() var volumeView : MPVolumeView = MPVolumeView() volumeView.frame = volumeViewHolder.frame volumeView.center = volumeViewHolder.center volumeView.sizeToFit() volumeView.showsRouteButton = false volumeView.showsVolumeSlider = true volumeView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight volumeViewHolder.addSubview(volumeView) } 
-2
source share

All Articles