2015-07-13 9 views
18

Ist es möglich, dynamisch zu färben statusBar, die in der neuen Apple Music App ist?Gleiche dynamische Statusleiste wie in der neuen Apple Music App

enter image description here

Edit:

Das neue Apple Musik-App in iOS 8.4 hat diese Funktion.

  • Öffnen Sie die App.
  • Wählen und spielen Sie einen Song (Statusleiste ist weiß)
  • Swipe Player Controller nach unten, um "Meine Musik" Controller zu sehen (es hat schwarze Statusleiste, vielleicht müssen Sie in der Navigation Hierarchie zurückgehen).
  • Jetzt wischen Sie einfach nach oben/unten, um dynamische Statusleistenänderungen zu sehen.

Edit 2:

Apple-Dokumentation scheint nicht zu uns es jetzt verwenden zu lassen (iOS 8.4). Wird wahrscheinlich in der Zukunft mit iOS 9 verfügbar sein.

bearbeiten 3: Does scheint noch nicht in iOS 9 verfügbar zu sein.

+0

Nachdenken über privates API –

+0

Image: Top weiß Teil die erste VC ist ein Teil mit weißen statusBar und Album-Cover ist ein weiterer VC (über dem ersten VC). –

+0

Das Problem ist, dass die Statusleiste dunkle Farbe in der oberen Hälfte und helle Farbe im unteren Teil hat? –

Antwort

-1

Denken Sie darüber nach, wie Sie dies ohne private APIs implementieren können.

Ich denke, es kann eine Lösung mit zweiten UIWindow überlagern Ihre StatusBar.

Adding view on StatusBar in iPhone

Vielleicht ist es möglich, Screenshots der Statusleiste ständig auf dem Bild (von Ihrem Hauptfenster genommen) zu machen, auf sie einige Filter anwenden und tragen dieses ‚fake statusbar Bild‘ auf dem zweiten Fenster (oben ' echte 'statusBar).

Und Sie können tun, was Sie mit der zweiten "falschen" Statusleiste wollen.

0

Auf den ersten Blick sah es aus wie eine Manipulation eines Snapshots aus der Statusleiste, aber die Statusleiste ist an beiden Enden live, also ist das nicht der Fall.

Auf den zweiten Blick sah es aus wie eine neue API, die in iOS 8.4 eingeführt wurde, aber nach Überprüfung der API konnte ich nichts dazu finden.

Es scheint mir sehr seltsam, dass Apple private apis in ihrer eigenen App verwenden würde. Dies würde einige wirklich schlechte Beispiele für Entwickler ergeben, aber andererseits gibt es nichts öffentliches, das Ihnen zwei Stile in Ihrer Live-Statusleiste zur Verfügung stellt.

Dies hinterlässt uns mit privater api oder schwarzer Magie.

+0

Also, wie kann dies mit schwarzer Magie oder private API getan werden? –

+0

Warum wäre es seltsam, wenn Apple private APIs verwendet? – Legoless

+0

Ich bin mir sicher, dass Apple hinter der Bühne eine Menge private Sachen benutzt, aber für etwas visuelles, das Entwickler offensichtlich nicht benutzen können .. fühlt sich ein bisschen wie ich an. – Segev

2

Ich bin zu 99,99% sicher, dass dies nicht mit öffentlichen API (leicht) gemacht werden kann, weil ich fast alles versucht habe (ich persönlich glaube auch nicht, dass es eine magische Methode ihrer Statusleiste ist, sondern ihre Anwendung ist in der Lage, Statusleiste Ansicht zu erhalten und dann nur Maske auf sie anwenden).

Was ich sicher bin ist, dass Sie Ihre eigene StatusBar tun können und es gibt MTStatusBarOverlay Bibliothek dafür, leider sehr alt, so dass ich nicht wirklich sagen kann, ob das funktioniert, aber es scheint, dass es immer noch Leute, die es verwenden .

Aber mit der Art und Weise Bibliothek tut es, ich denke, es könnte eine Lösung, die sicher ist, erfordert viel Arbeit, aber ist machbar, wenn auch nicht "live". Auf den Punkt gebracht das Sie tun würden:

  • Nehmen Sie Screenshot der 20 Pixel (Statusleiste)
  • Von diesem Screenshot, remove everything that is not black (Sie können es verbessern, so dass es für schwarze Ränder sucht, auf diese Weise können Sie grün bewahren Batterie und Transparenz) Dies wird Ihre Overlay-Maske und auch gefälschte Statusleiste macht
  • Overlay statusbar mit: Hintergrund Ansicht tatsächliche Statusleiste Maskierung und das alpha-Bild, das Sie gerade
  • erstellt
  • Apply mask to that image, alles, was maskiert ist, wird die Farbe ändern zu Schattierungen von Weiß
  • Ändern Sie die Höhe der Maske je nach Benutzer scroll

Jetzt sollten Sie in der Lage sein, ordnungsgemäß zu scrollen und die Farbe richtig zu ändern. Das einzige Problem, dass es verlässt, ist, dass Statusleiste nicht am Leben ist, aber ist es wirklich? Sobald Sie einen Bildlauf ausgeführt haben, entfernen Sie das Overlay sofort und lassen es auffrischen. Sie werden das Gleiche tun, wenn Sie zum oberen Bereich blättern, aber in diesem Fall ändern Sie die Farbe der Statusleiste in Weiß (keine Animation), so dass es Ihrem Zustand entspricht. Es wird nicht nur für eine kurze Zeit leben.

Hoffe es hilft!

+0

Für wen auch immer downvoted diese Antwort, könnten Sie bitte Ihre Meinung darüber, warum es schlecht ist, damit wir es iterieren können? Vielen Dank! –

+0

Diese Antwort wurde belohnt, weil sie eine private API-Meinung bietet und auch ein Beispiel liefert (auch wenn es quälend ist). Wahre Antwort ist, dass wir alle wahrscheinlich darauf warten müssen. –

+0

Sie verwenden private API für die animierende In/Out-Tabellenansicht A-Z-Index, es ist definitiv nicht öffentlich verfügbar, und es gibt Präzedenzfall in Apple Music für private UI Dinge :( – Luke

2

Wenn ich auf Jiris Antwort antworte, wird dich das ziemlich nahe bringen. Stellvertreter MTStatusBarOverlay mit CWStatusBarNotification. Um den modalen Übergang zwischen Ansichtscontrollern zu handhaben, verwende ich MusicPlayerTransition. Wir nehmen eine Bildansicht an: "art" in self.view mit frame: CGRect (0, 0, self.view.bounds.size.width, self.view.bounds.size.width). Muss ein wenig massieren, aber Sie bekommen das Wesentliche. Hinweis: Obwohl wir nicht "leben", sind die meisten, die wir jemals aus sind, eine Sekunde, und die Batteriefarbe ist nicht erhalten. Außerdem müssen Sie die Animationszeit in CWStatusBarNotification.m auf Null setzen. (notificationAnimationDuration-Eigenschaft)

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

Um die Statusleiste Screenshots synchron mit der aktuellen Statusleiste zu halten, Setup Ihrer Timer. Feuern Sie es in viewWillAppear, und töten Sie es in 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; 
} 

Weil wir einen starken Bezug auf den Timer und das Setup und die Entwertung geschieht auf dem gleichen Thread haben, gibt es keine Sorgen um den Timer zu entkräften scheitern. Das Endergebnis sollte wie folgt aussehen:

enter image description here

Verwandte Themen