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

时间:2015-07-13 08:25:13

标签: ios objective-c user-interface dynamic statusbar

是否可以在新的 Apple Music应用中动态着色Module Module1 Sub Main() Dim State As Action State = AddressOf DoSomething1 State() State = AddressOf DoSomething2 State() Console.ReadLine() 'all the above works fine. StateProp = AddressOf DoSomething1 'StateProp() ' **************** uncommenting this call causes compilation error: Property access must assign To the Property Or use its value. Console.ReadLine() End Sub 'trying to use this bit fails. Dim m_checkstate As Action Public Property StateProp() As Action Get Return m_checkstate End Get Set Console.WriteLine("Changing state. Do some work") m_checkstate = Value End Set End Property Sub DoSomething1() Console.WriteLine("Something1") End Sub Sub DoSomething2() Console.WriteLine("Something2") End Sub End Module

enter image description here

编辑:

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

  • 打开应用程序。
  • 选择并播放歌曲(状态栏为白色)
  • 向下滑动播放器控制器以查看“我的音乐”控制器(它有黑色状态栏,可能您必须返回导航层次结构)。
  • 现在只需向上/向下滑动即可查看动态状态栏更改。

编辑2:

Apple文档似乎不允许我们立即使用它(statusBar)。将来可能会iOS 8.4使用。

编辑3: 似乎尚未在iOS 9中提供。

4 个答案:

答案 0 :(得分:3)

我99.99%肯定这不能使用公共API(轻松)完成,因为我几乎尝试了所有的东西(我个人也不认为这是他们状态栏的一些神奇方法,而是他们的应用程序能够检索状态栏视图,然后只需应用蒙版)。

我确信你可以做你自己的StatusBar并且有MTStatusBarOverlay库,不幸的是很老的,所以我无法确定它是否有效,但似乎还有人谁使用它。

但是使用库的方式,我认为可能存在确定的解决方案,需要大量的工作,但是可行,但不是“实时”。简而言之,你会这样做:

  • 截取前20个像素(状态栏)
  • 的屏幕截图
  • 从该截图中remove everything that is not black(您可以对其进行改进,以便搜索黑色边缘,这样可以保留绿色电池和透明度)这将使您的叠加遮罩和假状态栏
  • 覆盖状态栏:背景视图屏蔽实际状态栏,以及您刚刚创建的Alpha图像
  • Apply mask to that image,被遮盖的所有内容都会将颜色更改为白色阴影
  • 根据用户滚动更改蒙版的高度

现在您应该能够正确滚动并正确更改颜色。它留下的唯一问题是状态栏不存在,但它真的存在吗?滚动后,立即删除叠加层,让它刷新。当您滚动到最顶部时,您将执行相同的操作,但在这种情况下,您将状态栏的颜色更改为白色(无动画),因此它适合您的状态。它只会在短时间内不活动。

希望它有所帮助!

答案 1 :(得分:2)

迭代Jiri的回答,这会让你非常接近。用MTStatusBarOverlay替换CWStatusBarNotification。为了处理视图控制器之间的模态转换,我使用MusicPlayerTransition。我们假设一个imageView:" art" in self.view with frame: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'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中触发它,并在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;
}

因为我们对计时器和设置有强烈的引用并且在同一个线程上发生了失效,所以不必担心计时器无法失效。 最终结果应如下所示:

enter image description here

答案 2 :(得分:0)

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

乍一看,它看起来像iOS 8.4中引入的一些新api,但在查看api之后,我找不到任何与之相关的内容。

对我来说,苹果在她自己的应用程序中使用私有api似乎很奇怪。这会给开发人员带来一些非常糟糕的例子,但是再一次,没有公开可以让你在你的实时状态栏上有两种风格。

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

答案 3 :(得分:-1)

考虑如何在没有私有API的情况下实现此目的。

我认为第二个UIWindow可能会覆盖你的statusBar。

Adding view on StatusBar in iPhone

也许可以不断制作状态栏的截图(从主窗口拍摄)到图像,在其上应用一些过滤器并显示这个“假状态栏图像”&#39;在你的第二个窗口(上面&#39;真正的&#39; statusBar)。

你可以用第二个&#34;假的&#34;做你想做的事。状态栏。