与新的Apple Music应用程序中的dynamic状态栏相同

是否有可能dynamic着色在新的苹果音乐应用程序中的statusBar

在这里输入图像说明

编辑:

iOS 8.4中新的Apple Music应用程序具有此function。

  • 打开应用程序。
  • select并播放一首歌曲(状态栏是白色的)
  • 向下滑动播放器控制器,看到“我的音乐”控制器(它有黑色的状态栏,也许你将不得不导航层次结构)。
  • 现在只需向上/向下滑动即可查看dynamic状态栏更改。

编辑2:

苹果的文档似乎并没有让我们现在使用它( iOS 8.4 )。 将来可能会iOS 9

编辑3:似乎还没有在iOS 9可用。

我99.99%肯定这不能使用公共API(很容易),因为我几乎所有的东西都尝试自己(我个人也不认为这是他们的状态栏的一些神奇的方法,而是他们的应用程序是能够检索状态栏视图,然后将掩码应用到它)。

我可以肯定的是,你可以做你自己的StatusBar,并有MTStatusBarOverlay库,不幸的是很旧,所以我不能确定它是否有效,但似乎仍然有人使用它。

但是使用图书馆的方式,我认为可能有解决scheme,肯定需要大量的工作,但是可行的,虽然不是“活”。 简而言之,你会这样做:

  • 截取前20个像素(状态栏)
  • 从该屏幕截图中, 删除所有不是黑色的东西 (可以改进它,以便search黑色边缘,这样可以保持绿色电池和透明度)这将使您的覆盖掩码和假状态栏
  • 覆盖状态栏:背景视图屏蔽实际状态栏,以及刚刚创build的alpha图像
  • 将遮罩应用于该图像 , 遮罩的所有内容都会将颜色更改为白色阴影
  • 根据用户滚动更改蒙版的高度

现在,您应该可以正确滚动并正确更改颜色。 它留下的唯一问题是状态栏不活着,但它是真的吗? 一旦你滑出,你立即删除你的覆盖,让它刷新。 当您滚动到顶部时,您将执行相同的操作,但是在这种情况下,您将状态栏的颜色更改为白色(无animation),因此它适合您的状态。 它只会在短时间内停止。

希望能帮助到你!

对Jiri的答案进行迭代,这会让你非常接近。 用CWStatusBarNotificationreplaceMTStatusBarOverlay 。 为了处理视图控制器之间的模式转换,我使用MusicPlayerTransition 。 我们在self.view中使用frame:CGRect(0,0,self.view.bounds.size.width,self.view.bounds.size.width)假设imageView:“art”。 需要一点点按摩,但你得到的主意。 注意:虽然我们不是“现场”,但是我们所做的最多的时间是一秒钟,电池的颜色不会保留。 另外,您需要将CWStatusBarNotification.m中的animation时间设置为零。 (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; } 

要使您的状态栏屏幕截图与实际状态栏保持同步,请设置您的计时器。 在viewWillAppear中启动它,并在viewDidDagappear中将其终止。

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

因为我们有一个很强的定时器的参考和设置和无效发生在同一个线程,不必担心定时器失效。 最终的结果应该是这样的:

在这里输入图像说明

乍看起来,它看起来像是从状态栏快照的操纵,但两端的状态栏是活的,所以情况并非如此。

乍一看,它看起来像一些新的API在iOS 8.4中引入,但在审查API后,我找不到任何相关的东西。

我觉得苹果在自己的应用程序中会使用私有API,这似乎很奇怪。 这对于开发人员来说会造成一些非常糟糕的例子,但是再一次,没有任何公开的内容会让你在状态栏上有两种样式。

这给我们留下了私人api或黑魔法。

思考如何在没有私有API的情况下实现这一点。

我认为可能有第二个UIWindow覆盖您的statusBar的解决scheme。

在iPhone上添加StatusBar视图

也许可以不断地从状态栏(从你的主窗口取得)截图,在其上应用一些filter,并在第二个窗口(在“真实”状态栏上方)显示这个“假状态栏图像”。

你可以用第二个“假”状态栏做你想做的事情。