MPVolumeView неправильно перерисовывается в iOS 7

Итак, у меня есть приложение iOS7, в котором я использую MPVolumeView, чтобы пользователь мог контролировать уровень громкости. У меня есть кнопка маршрута, скрытая на этом конкретном MPVolumeView, и я использую другой MPVolumeView с отключенным ползунком в качестве значка AirPlay.

Мое приложение поддерживает как книжную, так и альбомную ориентацию, а ползунок громкости имеет разную ширину в этих двух разных режимах.

Если вид сначала инициализируется в ландшафтном режиме, тогда MPVolumeView изменит свой размер правильно.

Однако, когда вид инициализируется в портретном режиме, а затем я перехожу в альбомный режим, ВСЕ остальное в приложении изменяется/перемещается, за исключением только перемещения MPVolumeView, и оно не становится короче, как должно.

Я использую пользовательские изображения на MPVolumeView, и если я удалю пользовательские изображения для трека, эта проблема исчезнет.

Вот код, используемый для инициализации 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's frame changes (i.e. 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's 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];

И в layoutSubviews я перемещаю/масштабирую его рамку:

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

Вот как это выглядит, когда вид начинается в портретном режиме: (общая ширина 640 пикселей)

Портретный режим

И когда он поворачивается в ландшафт, он выглядит так: (общая ширина 568 пикселей)

Ландшафтный режим

У кого-нибудь есть идеи?


person jrwagz    schedule 07.10.2013    source источник


Ответы (4)


Я испытываю ту же проблему. Мой текущий обходной путь — уничтожить и повторно добавить ползунок после поворота экрана:

- (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];
}

При этом все еще остается проблема, что ползунок все еще отображается с глюками ВО ВРЕМЯ вращения. Вот почему я визуализирую ползунок в изображение и заменяю ползунок указанным изображением до того, как произойдет вращение:

- (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];
}

Я знаю, что это не настоящее решение, но я думаю, что это лучше, чем ничего, и я надеюсь, что это поможет вам.

person Daniel S    schedule 19.10.2013
comment
Приятно осознавать, что это были не только мы! Спасибо, что поделились своим решением. - person jrwagz; 19.10.2013

После долгих экспериментов оказалось, что MPVolumeView сильно глючит в iOS 7.0.x.

У меня возникла проблема, когда наличие пользовательских изображений для ползунков min/max, большого пальца громкости и маршрута иногда приводило к тому, что ползунок расширялся, чтобы закрыть кнопку маршрута.

Когда я изменил порядок их вызова, проблема была устранена. Хитрость заключается в том, чтобы сначала вызвать setVolumeThumbImage и setRouteButtonImage, затем setMinimumVolumeSliderImage и setMaximumVolumeSliderImage.

Это позаботилось о перекрывающемся треке над кнопкой маршрута.

ОДНАКО это вызвало новую проблему, из-за которой maxSliderImage начал перекрывать объемThumbImage!

Окончательное решение состояло в том, чтобы вывести слой большого пальца на передний план следующим образом в подклассе 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;
        }
    }];
}
person igz    schedule 31.01.2014

Я протестировал другое решение, которое отлично работает. Я вставляю MPVolumeView в простой UIView (который можно создать в раскадровке). Затем я наблюдаю за ключом-значением свойства «границы» UIView. Когда это свойство изменяется, я обновляю фрейм MPVolumeView до новых границ UIView.

Это решение поддерживает как авторотацию, так и авторазметку.

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
    }
}

}
person Edouard    schedule 04.04.2015

person    schedule
comment
может быть, добавить объяснение, а не просто кучу кода - person Epicblood; 02.06.2015