GIKPopoverBackgroundView 是一个 UIPopoverBackgroundView 子类,它使用类似于 UIKit 中的图像来自定义 UIPopoverController 的背景。
与其他大多数第三方实现不同,GIKPopoverBackgroundView 不使用单独的背景和箭头图像,因此所有方向的外观都是无缝的。
下面的图示显示了 Apple 默认实现使用的背景图像。
当弹出窗口锚定到视图的角或边的控制或矩形时,使用 DownRight、UpRight、SideBottom 和 SideTop 图像。实心深蓝色区域中的点定义为使用标准 UIEdgeInsets
的可扩展区域。
Down、Up 和 Side 图像需要特殊处理。为了绘制带有水平居中上箭头的背景,Up 图像必须拉伸两次 - 一次在一侧箭头外。
箭头是在 Photoshop 中使用形状图层和图层样式创建的。每个箭头都包含 1x 和 2x 形状。每个图层的注释记录了渐变叠加样式的颜色停止和值。
文件已准备好 Slicy,以便轻松导出为 .PNG。
这个简短的屏幕录制演示了从各种锚点绘制了多个方向上的弹出窗口。弹出控制器中的导航栏和工具栏采用了与背景视图相同的外观,无需额外开发工作。
毫不奇怪,整个过程中都充分利用了 UIImage 的 -resizableImageWithCapInsets:
方法。对于需要两次拉伸操作的弹出窗口,将盖帽内边距盲目应用两次是不行的。指定可伸缩区域不会影响底层图像,直到在上下文或视图中绘制图像。
GIKPopoverBackgroundView 使用的方法是在基于位图的图形上下文中以适当的大小绘制可伸缩图像。
- (UIImage *)imageFromImageContextWithSourceImage:(UIImage *)image size:(CGSize)size
{
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
[image drawInRect:(CGRect){ .origin = CGPointZero, .size = size }];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
向结果图像添加第二组边距,然后将此新的可伸缩图像应用到弹出窗口背景的 UIImageView
上。
相同的技巧也适用于任何面向左边的“侧”箭头、' UpLeft' 或 'DownLeft' 箭头的弹出背景。源图像在应用了帽子内嵌之前进行了水平翻转并绘制到一个位图图形上下文中
- (UIImage *)mirroredImage:(UIImage *)image
{
UIImage *mirror = [UIImage imageWithCGImage:image.CGImage scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUpMirrored];
return [self imageFromImageContextWithSourceImage:mirror size:mirror.size];
}
如果部署目标是 iOS 5.x,则子类于 UIPopoverBackgroundView 的背景阴影不起作用。如果检测到 iOS 5,则使用背景层的 shadowPath
绘制阴影。
在 iOS 5 中,CALayer
的 shadowPath
属性不响应诸如一个图层边界的更改之类的隐式动画。如果弹出视图的几何形状发生变化,必须将显式动画添加到背景层以动画化阴影。
关于 UIPopoverBackgroundView
的文档指出 -setArrowOffset:
被调用于由 UIPopoverController 管理的动画块中。这似乎是同步边框和 shadowPath 动画的最佳位置。当调用 -setArrowOffset:
时,我们会检查图层的 animationKeys
数组中是否存在 bounds
键。如果找到,我们知道背景的框架正在变化——可能是由于键盘出现或消失。我们将边框动画的 timingFunction
和 duration
属性应用于新的 CABasicAnimation
for the shadowPath
.
- (void)addShadowPathAnimationIfNecessary:(CGPathRef)pathRef
{
NSArray *animationKeys = [self.popoverBackground.layer animationKeys];
if ([animationKeys containsObject:@"bounds"])
{
CAAnimation *boundsAnimation = [self.popoverBackground.layer animationForKey:@"bounds"];
CABasicAnimation *shadowPathAnimation = [CABasicAnimation animationWithKeyPath:@"shadowPath"];
shadowPathAnimation.toValue = [NSValue valueWithPointer:pathRef];
shadowPathAnimation.timingFunction = boundsAnimation.timingFunction;
shadowPathAnimation.duration = boundsAnimation.duration;
[self.popoverBackground.layer addAnimation:shadowPathAnimation forKey:@"shadowPath"];
}
}
要使用,将 GIKPopoverBackgroundView.h 和 GIKPopoverBackgroundView.m 添加到您的 Xcode 项目中。请随意使用提供的图像(在示例项目中找到)及其默认 UIEdgeInsets
值。在管理您的弹出控制器视图控制器中,设置弹出控制器的 popoverBackgroundViewClass
属性
popoverController = [(UIStoryboardPopoverSegue *)segue popoverController];
popoverController.popoverBackgroundViewClass = [GIKPopoverBackgroundView class];
包含的示例项目涵盖了若干场景,在这些场景中,当键盘出现时,源图像被拉伸两次、镜像和动画。
GIKPopoverBackgroundView 使用 ARC 并且需要 iOS 5.0 或更高版本。
GIKPopoverBackgroundView 由 Gordon Hughes 创建。
GIKPopoverBackgroundView 在 MIT 许可证下提供。有关更多信息,请参阅 LICENSE 文件。