Как переключить строку состояния с эффектом затухания в iOS7 (например, приложение "Фото" )?

Я хочу переключить видимость строки состояния при нажатии, точно так же, как в приложении "Фотографии".

До iOS 7 этот код работал хорошо:

-(void)setStatusBarIsHidden:(BOOL)statusBarIsHidden {

    _statusBarIsHidden = statusBarIsHidden;

    if (statusBarIsHidden == YES) {

        [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];


    }else{

        [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];

    }

}

Но я не могу заставить его работать в iOS 7. Все ответы, которые я нашел, предлагают только предложения для постоянного скрытия панели, но не для переключения.

Тем не менее, должно быть так, как это делает Photos.

Ответ 1

По умолчанию на iOS 7 или выше, чтобы скрыть строку состояния для определенного контроллера представлений, сделайте следующее:

  • если диспетчер представлений, который вы хотите скрыть строку состояния, представляется в виде модально, а modalPresentationStyle не UIModalPresentationFullScreen, вручную установите modalPresentationCapturesStatusBarAppearance на YES на представленном контроллере до его представления (например, в -presentViewController:animated:completion или -prepareForSegue:, если вы используете раскадровки)
  • переопределить -prefersStatusBarHidden в представленном контроллере и вернуть соответствующее значение
  • вызов setNeedsStatusBarAppearanceUpdate на представленном контроллере

Если вы хотите оживить его появление или исчезновение, сделайте шаг три в блоке анимации:

[UIView animateWithDuration:0.33 animations:^{
    [self setNeedsStatusBarAppearanceUpdate];
}];

Вы также можете установить стиль анимации, возвратив соответствующее значение UIStatusBarAnimation из -preferredStatusBarUpdateAnimation в представленном контроллере.

Ответ 2

Я смог упростить @Jon ответ и по-прежнему не могу отличить от приложения Photos в iOS 7. Похоже, что отложенное обновление при показе не требуется.

- (IBAction)toggleUI:(id)sender {
    self.hidesUI = !self.hidesUI;

    CGRect barFrame = self.navigationController.navigationBar.frame;

    CGFloat alpha = (self.hidesUI) ? 0.0 : 1.0;
    [UIView animateWithDuration:0.33 animations:^{
        [self setNeedsStatusBarAppearanceUpdate];
        self.navigationController.navigationBar.alpha = alpha;
    }];

    self.navigationController.navigationBar.frame = CGRectZero;
    self.navigationController.navigationBar.frame = barFrame;
}

- (BOOL)prefersStatusBarHidden {
    return self.hidesUI;
}

Ответ 3

Это можно считать немного взломанным, но это ближе всего к воспроизведению эффекта. Еще одна незначительная проблема. Когда вы исчезаете, вы можете видеть, что панель навигации изменяется с высоты. Это достаточно тонкое, но все же не идеальное исчезновение. Если кто-нибудь знает, как это исправить, дайте мне знать!

- (BOOL)prefersStatusBarHidden {

    if (_controlsAreHidden == YES)
        return YES;
    else
        return NO;
}

- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {

    return UIStatusBarAnimationFade;
}

-(void)setControlsAreHidden:(BOOL)controlsAreHidden {

    _controlsAreHidden = controlsAreHidden;

    if (controlsAreHidden == YES) {

        // fade out
        //

        CGRect barFrame = self.navigationController.navigationBar.frame;

        [UIView animateWithDuration:0.3 animations:^ {


            [self setNeedsStatusBarAppearanceUpdate];

            self.navigationController.navigationBar.alpha = 0;



        }];


        self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 44);



    }else{


        // fade in
        //

        CGRect barFrame = self.navigationController.navigationBar.frame;

        self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 64);

        [UIView animateWithDuration:0.3 animations:^ {

            [self setNeedsStatusBarAppearanceUpdate];

            self.navigationController.navigationBar.alpha = 1;

        }];


    }

}

Ответ 4

Сначала установите View controller-based status bar appearance в Info.plist на YES

Этот пример Swift показывает, как переключать StatusBar с помощью анимации после нажатия кнопки.

import UIKit

class ToggleStatusBarViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func prefersStatusBarHidden() -> Bool {
        return !UIApplication.sharedApplication().statusBarHidden
    }

    override func preferredStatusBarUpdateAnimation() -> UIStatusBarAnimation {
        return UIStatusBarAnimation.Slide
    }

    @IBAction func toggleStatusBar(sender: UIButton) {
        UIView.animateWithDuration(0.5,
            animations: {
                self.setNeedsStatusBarAppearanceUpdate()
        })
    }
}

Ответ 5

Этот код работает отлично:

-(void)setControlsAreHidden:(BOOL)controlsAreHidden {

    if (_controlsAreHidden == controlsAreHidden)
        return;

    _controlsAreHidden = controlsAreHidden;


    UINavigationBar * navigationBar = self.navigationController.navigationBar;

    if (controlsAreHidden == YES) {

        // fade out
        //

        CGRect barFrame = self.navigationController.navigationBar.frame;

        [UIView animateWithDuration:0.3 animations:^ {
            [self setNeedsStatusBarAppearanceUpdate];
            self.navigationController.navigationBar.alpha = 0;
        }];

        self.navigationController.navigationBar.frame = CGRectZero;
        self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 44);

    } else {

        // fade in
        //
        [UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^ {
            [self setNeedsStatusBarAppearanceUpdate];
        }];

        double delayInSeconds = 0.01;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

            [self.navigationController setNavigationBarHidden:NO animated:NO];
            navigationBar.alpha = 0;
            [UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^ {
                navigationBar.alpha = 1;
            }];

        });

    }

}

Ответ 6

На самом деле теперь необходимо испортить кадры навигационной панели. Вы можете добиться гладкой анимации, используя только два отдельных блока анимации. Что-то вроде этого должно работать нормально.

@property (nonatomic, assign) BOOL controlsShouldBeHidden;

...

- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated {

    if (self.controlsShouldBeHidden == hidden) {
        return;
    }

    self.controlsShouldBeHidden = hidden;

    NSTimeInterval duration = animated ? 0.3 : 0.0;

    [UIView animateWithDuration:duration animations:^(void) {

        [self setNeedsStatusBarAppearanceUpdate];

    }];

    [UIView animateWithDuration:duration animations:^(void) {

        CGFloat alpha = hidden ? 0 : 1;
        [self.navigationController.navigationBar setAlpha:alpha];

    }];

}

- (BOOL)prefersStatusBarHidden {

    return self.controlsShouldBeHidden;

}

Для совместимости с iOS 6 просто проверьте [self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]

Ответ 7

Способ решения этого вопроса зависит от значения параметра "Просмотр состояния панели управления на основе контроллера" в вашем приложении.

Если "Просмотр состояния панели управления на основе контроллера" NO в вашем plist, то этот код должен работать:

[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];

Если "Просмотреть состояние панели управления на основе контроллера" включено, в контроллерах вашего вида добавьте этот метод:

- (BOOL) prefersStatusBarHidden {
    // I've hardcoded to YES here, but you can return a dynamic value to meet your needs for toggling
    return YES;
}

Для переключения, когда вы хотите изменить, отображается ли строка состояния скрытой/показанной на основе значения вышеописанного метода, ваш контроллер просмотра может вызвать метод setNeedsStatusBarAppearanceUpdate.

Ответ 8

Чтобы устранить эту проблему при сдвиге навигационной панели при выцветании, вы должны добавить следующий код:

self.navigationController.navigationBar.frame = CGRectZero;

в разделе "затухание" перед следующей строкой кода:

self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 64);

Это необходимо, потому что кадр тот же, и установка того же кадра будет проигнорирована и не будет препятствовать скольжению панели навигации. Поэтому вам нужно изменить кадр на что-то другое, а затем снова установить его на правильный кадр, чтобы вызвать изменение.