Snapchat更新状态栏iOS7更新

问题描述 投票:0回答:2

在最新的 Snapchat 更新中,当您“向右滑动查看消息”时,状态栏会以一种渐变方式从黑色变为白色。

他们如何在不使用私有 API 的情况下做到这一点,或者他们正在使用私有 API?

我调查过的内容:

乍一看,我以为他们只是在做其他人所做的事情(就像 rdio 目前所做的那样),通过屏幕捕获整个屏幕,然后修改/动画该屏幕截图。

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

所以...我测试了它,当状态栏处于该状态(半黑/半白)时,它仍然处于活动状态,因为它不是屏幕截图,并且它响应充电变化/信号变化等,到目前为止据我所知,如果不使用私有 API,这是不可能的。

ios objective-c iphone-privateapi snapchat
2个回答
15
投票

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

您关于他们不使用屏幕截图的说法是错误的。您离开一侧的状态栏是快照,不会改变。一分钟过去后,您可以轻松地看到这一点,您要转换到的视图的新状态栏将会发生变化,因为它是真实的,但旧状态栏不会变化,因为它是快照。这使得这个错觉很容易实现。

以下是高层发生的事情:

  1. 当您开始转换时,应用程序会在当前状态下拍摄状态栏的快照(很可能它实际上是拍摄整个

    UIScreen
    的快照,并简单地从中裁剪掉状态栏)并附加一个窗口包含该快照到转换中视图的顶部,锚定到最终转换位置并被“来自”视图控制器的视图屏蔽。该窗口似乎在
    windowLevel
    上方有一个
    UIWindowLevelStatusBar
    ,因此它能够覆盖真实的状态栏。

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


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

我实际上可能有兴趣尝试让它发挥作用,所以如果是的话我会更新。

© www.soinside.com 2019 - 2024. All rights reserved.