我想以模态方式显示UIViewController
,并能够看到呈现它的视图的模糊版本。
遵循了许多类似的问题:
iOS 7 Translucent Modal View Controller
我在控制器的视图中添加了一个背景,该视图基于呈现控制器的捕获视图。我面临的问题是我的应用程序支持多个方向,当呈现和旋转模态视图时,底层背景图像不再匹配。
我尝试在模态viewController的didRotateFromInterfaceOrientation:
中抓取呈现viewController的新快照,但看起来呈现的viewController的UI没有被更新,结果图像仍然是错误的方向。有没有办法强制重绘一个被模态隐藏的视图?
答案 0 :(得分:1)
经过长时间的考虑,我想出了一个可行的方法来处理它。它的效果如何取决于您在呈现viewController
时所拥有的内容类型。
一般的想法是在呈现新的viewController
之前不要使用一个,而是两个屏幕截图 - 一个用于肖像,一个用于横向。这是通过更改顶部viewController
和导航栏(如果适用)的帧来模拟不同的方向,获取结果的屏幕截图并将其更改回来实现的。用户永远不会在设备上看到此更改,但屏幕抓取仍会显示新的方向。
具体代码取决于您调用它的位置,但主要逻辑是相同的。我的实现从AppDelegate
运行,因为它被UIViewController
的几个子类重用。
以下是获取相应屏幕截图的代码。
// get references to the views you need a screenshot of
// this may very depending on your app hierarchy
UIView *container = [self.window.subviews lastObject]; // UILayoutContainerView
UIView *subview = container.subviews[0]; // UINavigationTransitionView
UIView *navbar = container.subviews[1]; // UINavigationBar
CGSize originalSubviewSize = subview.frame.size;
CGSize originalNavbarSize = navbar.frame.size;
// compose the current view of the navbar and subview
UIImage *currentComposed = [self composeForeground:navbar withBackground:subview];
// rotate the navbar and subview
subview.frame = CGRectMake(subview.frame.origin.x, subview.frame.origin.y, originalSubviewSize.height, originalSubviewSize.width);
// the navbar has to match the width of the subview, height remains the same
navbar.frame = CGRectMake(navbar.frame.origin.x, navbar.frame.origin.y, originalSubviewSize.height, originalNavbarSize.height);
// compose the rotated view
UIImage *rotatedComposed = [self composeForeground:navbar withBackground:subview];
// change the frames back to normal
subview.frame = CGRectMake(subview.frame.origin.x, subview.frame.origin.y, originalSubviewSize.width, originalSubviewSize.height);
navbar.frame = CGRectMake(navbar.frame.origin.x, navbar.frame.origin.y, originalNavbarSize.width, originalNavbarSize.height);
// assign the variables depending on actual orientations
UIImage *landscape; UIImage *portrait;
if (originalSubviewSize.height > originalSubviewSize.width) {
// current orientation is portrait
portrait = currentComposed;
landscape = rotatedComposed;
} else {
// current orientation is landscape
portrait = rotatedComposed;
landscape = currentComposed;
}
CustomTranslucentViewController *vc = [CustomTranslucentViewController new];
vc.backgroundSnap = portrait;
vc.backgroundSnapLandscape = landscape;
[rooVC presentViewController:vc animated:YES completion:nil];
方法composeForeground:withBackground:
是一种便捷方法,可根据两个输入视图(导航栏+视图控制器)生成适当的背景图像。除了将两个视图组合在一起之外,在旋转呈现的viewController
时,使结果看起来更自然有点神奇。具体来说,它将屏幕截图扩展到1024x1024平方,并使用合成图像的镜像副本填充额外空间。在许多情况下,一旦模糊,这看起来就足够了,因为视图重新绘制的方向更改动画不可用。
- (UIImage *)composeForeground:(UIView *)frontView withBackground:(UIView *)backView {
UIGraphicsBeginImageContextWithOptions(backView.frame.size, 0, 0);
[backView.layer renderInContext:UIGraphicsGetCurrentContext()];
// translation is necessary to account for the extra 20 taken up by the status bar
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), frontView.frame.origin.x, frontView.frame.origin.y);
[frontView.layer renderInContext:UIGraphicsGetCurrentContext()];
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -frontView.frame.origin.x, -frontView.frame.origin.y);
// this is the core image, would have left it at this if we did not need to use fancy mirrored tiling
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// add mirrored sections
CGFloat addition = 256; // 1024 - 768
if (newImage.size.height > newImage.size.width) {
// portrait, add a mirrored image on the right
UIImage *horizMirror = [[UIImage alloc] initWithCGImage:newImage.CGImage scale:newImage.scale orientation:UIImageOrientationUpMirrored];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(newImage.size.width+addition, newImage.size.height), 0, 0);
[horizMirror drawAtPoint:CGPointMake(newImage.size.width, 0)];
} else {
// landscape, add a mirrored image at the bottom
UIImage *vertMirror = [[UIImage alloc] initWithCGImage:newImage.CGImage scale:newImage.scale orientation:UIImageOrientationDownMirrored];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(newImage.size.width, newImage.size.height+addition), 0, 0);
[vertMirror drawAtPoint:CGPointMake(0, newImage.size.height)];
}
// combine the mirrored extension with the original image
[newImage drawAtPoint:CGPointZero];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// for ios 6, crop off the top 20px
if (SYSTEM_VERSION_LESS_THAN(@"7")) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(newImage.size.width, newImage.size.height-20), NO, 0);
[newImage drawAtPoint:CGPointMake(0, -20)];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return newImage;
}
生成的横向和纵向图像可以根据需要进行模糊和着色,并设置为显示的viewController
的背景。使用此willRotateToInterfaceOrientation:duration:
的{{1}}方法选择合适的图片。
注意:我尝试尽可能减少在图像和图形上下文中完成的工作量,但在生成背景时仍然有一点延迟(大约每个30-90毫秒) viewController
迭代,取决于内容,在老式慢速iPad 2上。如果您知道进一步优化或简化上述解决方案的方法,请分享!