2015-07-13 19 views
18

Czy możliwe jest dynamiczne barwienie statusBar, które jest w nowej aplikacji Apple Music ?Ten sam dynamiczny pasek stanu, jak w nowej aplikacji Apple Music

enter image description here

Edit:

Nowa aplikacja Apple Music w iOS 8.4 ma tę funkcję.

  • Otwórz aplikację.
  • Wybierz i odtwórz utwór (pasek stanu jest biały)
  • Przesuń kontrolkę odtwarzacza w dół, aby zobaczyć kontroler "Moja muzyka" (ma czarny pasek stanu, być może będziesz musiał wrócić do hierarchii nawigacji).
  • Teraz wystarczy przesunąć palcem w górę/w dół, aby wyświetlić dynamiczne zmiany paska stanu.

Edit 2:

dokumentacji Apple nie wydaje się użyjmy go teraz (iOS 8.4). Będą dostępne prawdopodobnie w przyszłości pod numerem iOS 9.

Edit 3: Czy nie wydaje się być dostępny w iOS 9 jeszcze.

+0

myślą o prywatnych API –

+0

Image: Top biała część jest pierwszą VC, część z białym pasku stanu i okładki albumu jest kolejnym VC (powyżej pierwszego VC). –

+0

Problem polega na tym, że pasek stanu ma ciemny kolor w górnej połowie i jasny kolor w dolnej części? –

Odpowiedz

-1

Zastanawiasz się, jak zaimplementować to bez prywatnych interfejsów API.

Myślę, że może istnieć rozwiązanie z drugim UIWindow nakładającym Twój statusBar.

Adding view on StatusBar in iPhone

Może to możliwe, aby zrzuty ekranu pasku stanu stale (pobierane z głównego okna) do obrazu, zastosować jakiś filtr na nim i wyświetlić ten „fałszywy pasku stanu obrazu” na swoim drugim oknie (powyżej ' prawdziwy statusBar).

I możesz robić, co chcesz, z drugim "fałszywym" pasku stanu.

0

Na pierwszy rzut oka wyglądało to jak manipulacja migawką z paska stanu, ale pasek stanu jest wyświetlany na obu końcach, więc tak nie jest.

Na pierwszy rzut oka wyglądało to jak nowe api wprowadzone w iOS 8.4, ale po przejrzeniu api nie mogłem znaleźć nic z tym związanego.

Wydaje mi się dziwne, że Apple użyje prywatnych api we własnej aplikacji. Może to być bardzo zły przykład dla programistów, ale z drugiej strony nie ma nic publicznego, co pozwoli ci mieć dwa style na pasku statusu na żywo.

To pozostawia nam prywatną api lub czarną magię.

+0

Jak można to zrobić za pomocą czarnej magii lub prywatnego api? –

+0

Dlaczego byłoby dziwnie, gdyby Apple używał prywatnych API? – Legoless

+0

Jestem pewna, że ​​Apple używa wielu prywatnych rzeczy poza sceną, ale dla czegoś wizualnego, którego twórcy wyraźnie nie mogą użyć ... trochę mi to wychodzi. – Segev

2

Mam 99,99% pewności, że nie można tego zrobić za pomocą publicznego interfejsu API (łatwo), ponieważ próbowałem sobie prawie wszystkiego, co jest (osobiście też nie uważam, że jest to jakaś magiczna metoda ich paska stanu, ale zamiast tego ich aplikacja jest w stanie pobrać widok paska stanu, a następnie zastosować do niego maskę).

Jestem pewien, że możesz zrobić swój własny StatusBar i jest tam biblioteka MTStatusBarOverlay, niestety bardzo stara, więc nie mogę powiedzieć, czy to działa, ale wydaje się, że wciąż są ludzie, którzy go używają .

Ale korzystając z tego, jak robi to biblioteka, myślę, że może być pewne rozwiązanie, które wymaga dużo pracy, ale jest wykonalne, ale nie "na żywo". W skrócie wygląda to tak:

  • screenshota z najlepszych 20 pikseli (pasek stanu)
  • Od tego ekranu, remove everything that is not black (można ją poprawić, więc szuka czarnych krawędziach, w ten sposób można zachować zielony bateria i przezroczystość) To sprawi, że maska ​​nakładki, a także fałszywe pasek stanu
  • Nakładka statusbar z: view maskowania rzeczywistego pasek stanu tle, a po prostu stworzony
  • Apply mask to that image alfa obrazu, wszystko, co jest maskowany zmieni kolor odcienie bieli
  • Zmiana wysokości maski w zależności od przewijania użytkownika

Teraz powinieneś być w stanie prawidłowo przewijać i zmieniać kolor. Jedynym problemem, który pozostawia, jest to, że pasek statusu nie jest żywy, ale czy to naprawdę? po przewinięciu natychmiast usuniesz nakładkę, pozwalając jej odświeżyć. Zrobisz to samo, gdy przewiniesz do samej góry, ale w takim przypadku zmienisz kolor paska stanu na biały (bez animacji), więc pasuje do twojego stanu. To nie będzie żyć tylko przez krótki okres czasu.

Mam nadzieję, że pomoże!

+0

Ktokolwiek zrzekł się tej odpowiedzi, czy mógłbyś przedstawić swoją opinię o tym, dlaczego jest ona zła, abyśmy mogli ją powtórzyć? Dziękuję Ci! –

+0

Uznanie tej odpowiedzi, ponieważ oferuje prywatną opinię na temat api, a także stanowi przykład (nawet jeśli jest to niepokojące). Prawdziwa odpowiedź brzmi, że wszyscy prawdopodobnie musimy na to poczekać. –

+0

Używają prywatnego interfejsu API do animowania widoku tabeli wejść/wyjść A-Z, to zdecydowanie nie jest publicznie dostępny, a Apple Music ma precedens do robienia prywatnych rzeczy interfejsu użytkownika :( – Luke

2

Powtórzenie odpowiedzi Jiri, to ci się bardzo zbliża. Zamień MTStatusBarOverlay na CWStatusBarNotification. Aby obsłużyć przejście modalne między kontrolerami widoku, używam MusicPlayerTransition. Przyjmujemy imageView: "art" w self.view z ramką: CGRect (0, 0, self.view.bounds.size.width, self.view.bounds.size.width). Potrzebuje trochę masowania, ale masz sens. Uwaga: chociaż nie jesteśmy "na żywo", najbardziej nam się zdarzy, że jest jedna sekunda, a kolor baterii nie jest zachowany. Musisz również ustawić czas animacji w CWStatusBarNotification.m na zero. (właściwość notificationAnimationDuration).

#import "CWStatusBarNotification.h" 

#define kStatusTextOffset  5.4 // (rough guess of) space between window's origin.y and status bar label's 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's color via the imageView's .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; 
} 

Aby zachować swój status screeny kreskowe w synchronizacji z rzeczywistą pasku stanu, konfiguracji Twój zegar. Uruchom go w viewWillAppear i zabij w 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; 
} 

Ponieważ mamy wyraźne odniesienie do zegara i konfiguracji oraz unieważnieniu dzieje się w tym samym wątku, nie ma obawy o zegarze upadającego unieważnić. Efekt końcowy powinien wyglądać mniej więcej tak:

enter image description here