Подтвердить что ты не робот

Та же динамическая строка состояния, что и в новом приложении Apple Music

Возможно ли иметь динамическую раскраску statusBar, которая находится в новом приложении Apple Music?

enter image description here

Edit:

Новое приложение Apple Music в iOS 8.4 имеет эту функцию.

  • Откройте приложение.
  • Выбор и воспроизведение песни (строка состояния белая)
  • Проведите контроллер плеера вниз, чтобы увидеть контроллер "Моя музыка" (он имеет черную строку состояния, возможно, вам придется вернуться в иерархию навигации).
  • Теперь просто проведите пальцем вверх/вниз, чтобы увидеть изменения динамической строки состояния.

Изменить 2:

Документация Apple, похоже, не позволяет нам использовать ее прямо сейчас (iOS 8.4). Будет доступно, вероятно, в будущем с iOS 9.

Изменить 3: Кажется, что он еще не доступен в iOS 9.

4b9b3361

Ответ 1

Я уверен, 99 99.99% это невозможно сделать с помощью общедоступного API (легко), потому что я попробовал почти все, что есть (я лично также не думаю, что это какой-то магический метод их строки состояния, но вместо этого их применение может извлекать представление строки состояния, а затем просто применять к нему маску).

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

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

  • Сделайте снимок экрана с верхними 20 пикселями (строка состояния)
  • Из этого снимка экрана удалите все, что не черное (вы можете улучшить его, чтобы он искал черные края, таким образом вы можете сохранить зеленую батарею и прозрачность). сделают вашу оверлейную маску, а также фальшивую строку состояния
  • Наложение статусной строки с помощью: фонового представления, маскирующего фактическую строку состояния, и только что созданного альфа-изображения.
  • Применить маску к этому изображению, все, что замаскировано, изменит цвет на оттенки белого
  • Изменить высоту маски в зависимости от прокрутки пользователя

Теперь вы должны иметь возможность правильно прокручивать и правильно менять цвет. Единственная проблема, с которой он уходит, - это то, что строка состояния не жива, но действительно ли это? как только вы прокрутите страницу, вы сразу же удалите свой оверлей, давая ему возможность обновиться. Вы будете делать то же самое, когда прокрутите до самого верха, но в этом случае вы измените цвет строки состояния на белый (без анимации), чтобы он соответствовал вашему состоянию. Он не будет жить только в течение короткого периода времени.

Надеюсь, что это поможет!

Ответ 2

Итерация на ответ Jiri, это приблизит вас. Замените MTStatusBarOverlay с CWStatusBarNotification. Чтобы обрабатывать модальный переход между контроллерами представлений, я использую MusicPlayerTransition. Мы принимаем imageView: "art" в self.view с фреймом: CGRect (0, 0, self.view.bounds.size.width, self.view.bounds.size.width). Нужно немного массировать, но вы получаете суть. Примечание. Хотя мы не "живые", большинство из нас когда-либо отключится, это одна секунда, а цвет батареи не сохраняется. Кроме того, вам нужно установить время анимации в CWStatusBarNotification.m равным нулю. (свойство notificationAnimationDuration).

#import "CWStatusBarNotification.h"

#define kStatusTextOffset       5.4  // (rough guess of) space between window origin.y and status bar label origin.y

@interface M_Player () <UIGestureRecognizerDelegate> 

@property (retain) UIView *fakeStatusBarView;
@property (retain) CWStatusBarNotification *fakeStatusBar;
@property (retain) UIImageView *statusImgView;
@property (retain) UIImageView *statusImgViewCopy;
@property (retain) UIWindow *window;
@property (strong, nonatomic) NSTimer *statusTimer;

@end

@implementation M_Player
@synthesisze fakeStatusBarView, fakeStatusBar, statusImgView, statusImgViewCopy, window, statusTimer;

-(void)viewDidLoad{

    self.window = [[UIApplication sharedApplication] delegate].window;

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleStatusBarDrag:)];
    pan.delegate = self;
    [self.view addGestureRecognizer:pan];
}

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    if (!fakeStatusBar){
        [self buildFakeStatusBar];
    }

    if (!statusTimer) {
        [self setupStatusBarImageUpdateTimer];
    }

     // optional
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
    [self setNeedsStatusBarAppearanceUpdate]; 


-(void)viewDidDisappear:(BOOL)animated{
   [super viewDidDisappear:animated];

   [self destroyStatusBarImageUpdateTimer];

}

-(void)destroyFakeStatusBar{
    [statusImgView removeFromSuperview];
    statusImgView = nil;
    [fakeStatusBarView removeFromSuperview];
    fakeStatusBarView = nil;
    fakeStatusBar = nil;
}

-(void)buildFakeStatusBar{
    UIWindow *statusBarWindow =  [[UIApplication sharedApplication] valueForKey:@"_statusBarWindow"];  // This window is actually still fullscreen. So we need to capture just the top 20 points.

    UIGraphicsBeginImageContext(self.view.bounds.size);
    [statusBarWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CGRect rect = CGRectMake(0, 0, self.view.bounds.size.width, 20);
    CGImageRef imageRef = CGImageCreateWithImageInRect([viewImage CGImage], rect);
    UIImage *statusImg = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    statusImg = [statusImg imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];  // This allows us to set the status bar content color via the imageView .tintColor property

    statusImgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
    statusImgView.image = statusImg;
    statusImgView.tintColor = [UIColor colorWithWhite:0.859 alpha:1.000];  // any color you want

    statusImgViewCopy = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
    statusImgViewCopy.image = statusImg;
    statusImgViewCopy.tintColor = statusImgView.tintColor;


    fakeStatusBarView = nil;
    fakeStatusBar = nil;
    fakeStatusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
    [fakeStatusBarView addSubview:statusImgView];

    fakeStatusBar = [CWStatusBarNotification new];
    fakeStatusBar.notificationStyle = CWNotificationStyleStatusBarNotification;
    [fakeStatusBar displayNotificationWithView:fakeStatusBarView forDuration:CGFLOAT_MAX];
}

-(void)handleStatusBarDrag:(UIPanGestureRecognizer*)gestureRecognizer{
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {

    }

    if (gestureRecognizer.state == UIGestureRecognizerStateChanged){
        CGPoint convertedPoint = [self.window convertPoint:art.frame.origin fromView:self.view];
        CGFloat originY = convertedPoint.y - kStatusTextOffset;

        if (originY > 0 && originY <= 10) {  // the range of change we're interested in
            //NSLog(@"originY:%f statusImgView.frame:%@", originY, NSStringFromCGRect(statusImgView.frame));

            // render in context from new originY using our untouched copy as reference view
            UIGraphicsBeginImageContext(self.view.bounds.size);
            [statusImgViewCopy.layer renderInContext:UIGraphicsGetCurrentContext()];
            UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            CGRect rect = CGRectMake(0, kStatusTextOffset + originY, self.view.bounds.size.width, 20);
            CGImageRef imageRef = CGImageCreateWithImageInRect([viewImage CGImage], rect);
            UIImage *statusImg = [UIImage imageWithCGImage:imageRef];
            CGImageRelease(imageRef);

            statusImgView.image = statusImg;
            statusImgView.transform = CGAffineTransformMakeTranslation(0, kStatusTextOffset + originY);

        }

       // destroy
        if (originY > 90) {
            [self destroyFakeStatusBar];
        }
    }

    if (gestureRecognizer.state == UIGestureRecognizerStateEnded){

    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

Чтобы синхронизировать снимки экрана состояния с фактической строкой состояния, настройте свой таймер. Огонь его в viewWillAppear и убейте его в viewDidDisappear.

-(void)setupStatusBarImageUpdateTimer{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^(){
            // main thread
            if (!statusTimer) {
                statusTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handleStatusTimer:) userInfo:nil repeats:YES];
                [[NSRunLoop currentRunLoop] addTimer:statusTimer forMode:NSRunLoopCommonModes];
            }
        });
    });
}

-(void)destroyStatusBarImageUpdateTimer{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^(){
            // main thread
            [statusTimer invalidate];
            statusTimer = nil;
        });
    });
}

-(void)handleStatusTimer:(NSTimer*)timer{
    UIWindow *statusBarWindow =  [[UIApplication sharedApplication] valueForKey:@"_statusBarWindow"];

    UIGraphicsBeginImageContext(CGSizeMake(self.view.bounds.size.width, 20));
    [statusBarWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CGRect rect = CGRectMake(0, 0, self.view.bounds.size.width, 20);
    CGImageRef imageRef = CGImageCreateWithImageInRect([viewImage CGImage], rect);
    UIImage *statusImg = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    statusImg = [statusImg imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    statusImgViewCopy.image = statusImg;
}

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

введите описание изображения здесь

Ответ 3

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

На первый взгляд это выглядело как новый api, который был представлен в iOS 8.4, но после просмотра api я не смог найти ничего, что связано с этим.

Мне кажется странным, что яблоко будет использовать private apis в своем приложении. Это может привести к действительному плохим примерам для разработчиков, но опять же, нет ничего публичного, что позволит вам иметь два стиля на вашей активной панели статусов.

Это оставляет нам частную апи или черную магию.

Ответ 4

Размышление о том, как реализовать это без частных API.

Я думаю, что может быть решение со вторым UIWindow, накладывающим ваш статусBar.

Добавление представления в StatusBar в iPhone

Возможно, вам удастся сделать скриншоты строки состояния постоянно (взяты из вашего основного окна) на изображение, применить к нему некоторый фильтр и отобразить это "изображение поддельной статусной строки" на вашем втором окне (выше "real" statusBar).

И вы можете делать то, что хотите, со второй "поддельной" стойкой статуса.