Snapchat更新状态栏iOS7更新。

7

在最新版的Snapchat中,当你“向右滑动查看消息”时,状态栏会渐变地从黑色变为白色。

http://imgur.com/osJI20u

他们是如何做到不使用私有API的,还是使用了私有API?

我所了解的:

乍一看,我以为他们只是像rdio目前所做的那样,通过屏幕捕捉整个屏幕,然后修改/动画化该截图。

但是...你无法从该屏幕捕捉方法中访问或动画UIImage,因此尽管您可以移动/裁剪该截图,但无法更改其外观。

所以...我进行了测试,当状态栏处于该状态(半黑/半白)时,它仍然是实时的,也就是说它不是一个截图,并且对充电变化/信号变化等作出响应,就我所知,这是不可能的,除非使用私有API。


也许他们正在重复截取状态栏并在过渡期间重复修改这些屏幕截图。或者也许他们正在检测电池/时间/信号的变化,然后仅在这些事物发生变化时进行屏幕截图。 - Lyndsey Scott
2
显然有一些深奥的魔法在起作用。 - CrimsonChris
2个回答

15

这让我很好奇,所以我稍微玩了一下该应用,并在越狱设备上使用Spark Inspector检查了应用程序的视图层次结构,发现了一些东西。

你关于他们不使用屏幕截图是错误的。你正在导航离开的那一侧的状态栏是一个快照,不会改变。当一分钟过去时,你要转换到的视图的新状态栏将会改变,因为它是实际的,但旧的状态栏不会改变,因为它是一个快照。这使得幻觉易于解决。

以下是高水平上发生的事情:

  1. 当你开始过渡时,该应用程序会对当前状态栏进行快照(最有可能实际上是对整个UIScreen进行快照,并从中简单地裁剪出状态栏),并将包含该快照的窗口附加到过渡顶部的视图上,锚定到最终过渡位置,并由 "from" 视图控制器的视图遮罩。该窗口似乎具有 windowLevel 高于 UIWindowLevelStatusBar,因此能够覆盖真正的状态栏。

  2. 然后,"to" 视图控制器接管了真正的状态栏,并将其更改为 "light content" 状态。这呈现出一个巧妙的幻觉,即单个状态栏随着过渡边界改变颜色,但实际的状态栏始终位于快照窗口下方,并且如果不是因为快照窗口位于其上方,它确实会在过渡开始时立即可见。


1
你说得对,从屏幕捕获的视图是来自于,而使用真实状态栏的视图是要去的,这就是让我感到困惑的原因。新窗口有点儿狡猾。我猜苹果只能反对这种行为,但仍然允许它! - Rob Parker
嗨Dima,好答案。你可能想看一下这个问题https://dev59.com/CF0Z5IYBdhLWcg3wfgi-。显然,新的音乐应用程序使用状态栏完成此操作,但两端都是实时的(不像Snapchat使用的快照)。 - Segev
感谢您的评论。看起来它很可能是私有API,但我也会关注那个问题。他们在下拉通知中心叠加层(其中状态栏与视图动态滑动下来)中已经拥有类似的功能至少一年了,所以他们肯定还有一些我们尚未获得的技巧。 - Dima

2

基于Dima的回答,我认为提供一些基本实现方法可以帮助人们入门。正如Dima所说,它并不是移动状态栏,而是移动状态栏的图像。

免责声明:我YOLO了这个实现,因此不能保证它是否可以直接使用。

从iOS7开始,您可以使用

UIView *screen = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];

基本上状态栏是从图片中裁剪出来,然后添加到一个UIWindow中,并设置windowLevel高于UIWindowStatusLevelBar,这样您就可以在真实的状态栏上方看到它。类似于以下内容:

UIWindow *statusBarWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
statusBarWindow.windowLevel = UIWindowLevelStatusBar + 1; // higher
statusBarWindow.hidden = NO; // fun fact you don't have to add a UIWindow to anything, just setting hidden = NO should display it
statusBarWindow.backgroundColor = [UIColor clearColor]; // since visible, make it clear until we actually add the screen shot to it so as to not block anything 

...

// The view that will hold the screen shot
// Later, I think we're going to need to play with contentInsets to make the view look like its staying still, so we're making it a UIScrollView in case
UIScrollView *statusBarView = [[UIScrollView alloc] initWithFrame:[UIApplication sharedApplication].statusBarFrame];
statusBarView.clipsToBounds = YES;

// Now we add the screen shot we took to this status bar view
[scrollingStatusBarView addSubview:statusBarView];
[statusBarView addSubview:screen]; // screen is the UIScrollView from previous code block (the screen shot)

// Now add this statusBarView with the image to the window we created
[statusBarWindow addSubview:statusBarView];

现在,其余的内容实际上取决于您的实现方式,但是从这里开始,您只需要使用平移或任何导致新视图从侧面进入的操作来处理移动视图。


当滑动开始时,请使用任何样式、背景等配置您的新状态栏(位于下方):

[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:NO];
// anything else you might feel like doing 

现在我们有一些有趣的事情发生了。随着过渡的进行,您将不得不使用它滚动到“剪切” statusBarView 的偏移量。 statusBarView.frame 必须从x = offset开始,否则它会显示在实际状态栏上方(因为它的窗口更高),但如果您不更改contentInsets,则图片将随着过渡而滑动。因此,为了创建幻觉,您需要将原点向右推,并将左侧内容插图增加相同的数量,以使其出现在同一位置。

因此,在处理滑动操作的任何方法内部:

注意:这将严重取决于个人实现,并且可能需要进行一些实验。

 // if you're using an animation to handle the transition you can match up this change in x position with the animation

// if you're doing via pan gesture you could try something like:
CGFloat offset = self.viewThatIsScrolling.contentOffset.x; // may be negative depending on direction that is being swiped

if (midTransition) { // you can use the offset value here to check
    // NOTE: again, will depend on which direction this animation is happening in. I'll assume a new view coming in from left (so swiping right)
    statusBarView.frame = CGRectMake(offset, 0, self.statusBarView.frame.width, 20.0f); // move the origin over to the right
    statusBarView.contentInsets = UIEdgeInsetsMake(0, offset, 0, 0); // make the picture start 'offset' pixels to the left so it'll look like it hasn't moved

} 
(顺便提一下:您也可以尝试使用转换而不是显式设置框架:statusBarView.transform = CGAffineTransformMakeTranslation(offset - previousOffsetAmount,0);。我在那里使用previousOffsetAmount,因为您只希望它按新的偏移量移动,以便它可以匹配。如果您选择这个方法,这将不得不以某种方式进行跟踪)

最后,在“滑动”完成后,不要忘记完全从窗口中删除截图:

// if continuing from before:
if (midTransition) {
    ...
} else { // the view has been slid completely
    [statusBarView removeFromSuperview];
    statusBarView = nil;
    // I'm keeping the UIWindow where it is for now so it doesn't have to be recreated and because it has a clear background anyways
}

// if this is being done via animation the above can be called the animation finishes

我真的很想尝试让它工作,如果成功了,我会更新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接