我正在创建iOS提供的popover的自定义布局。我已经将UIPopoverBackgroundView子类化,并让它为我的popover正确绘制背景。我的问题是现在UIPopoverController在popover上创建了一个内部阴影,影响了popover的contentViewController。我想删除这个内部阴影,所以只显示我的contentViewController的内容。
这是popover当前的外观,使用UILabel来演示对contentViewController的影响。
有没有办法删除这个内部阴影?
答案 0 :(得分:25)
在ios6.0中添加了对此的支持,并进行了以下调用:
+ (BOOL)wantsDefaultContentAppearance
答案 1 :(得分:10)
由于没有优雅的方法可以做到这一点,因为我不想重写整个UIPopoverController只是为了做到这一点,我创建了一个简单的hack,通过遍历UIView结构来删除popover上的内部阴影。 hack是UIPopoverController上的一个类别,我只是把它放在我的UIPopoverBackgroundView子类的文件中。所以这是代码:
@interface UIPopoverController(removeInnerShadow)
- (void)removeInnerShadow;
- (void)presentPopoverWithoutInnerShadowFromRect:(CGRect)rect
inView:(UIView *)view
permittedArrowDirections:(UIPopoverArrowDirection)direction
animated:(BOOL)animated;
- (void)presentPopoverWithoutInnerShadowFromBarButtonItem:(UIBarButtonItem *)item
permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections
animated:(BOOL)animated;
@end
@implementation UIPopoverController(removeInnerShadow)
- (void)presentPopoverWithoutInnerShadowFromRect:(CGRect)rect inView:(UIView *)view permittedArrowDirections:(UIPopoverArrowDirection)direction animated:(BOOL)animated
{
[self presentPopoverFromRect:rect inView:view permittedArrowDirections:direction animated:animated];
[self removeInnerShadow];
}
- (void)presentPopoverWithoutInnerShadowFromBarButtonItem:(UIBarButtonItem *)item
permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections
animated:(BOOL)animated
{
[self presentPopoverFromBarButtonItem:item permittedArrowDirections:arrowDirections animated:animated];
[self removeInnerShadow];
}
- (void)removeInnerShadow
{
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
for (UIView *windowSubView in window.subviews)
{
if ([NSStringFromClass([windowSubView class]) isEqualToString:@"UIDimmingView"])
{
for (UIView *dimmingViewSubviews in windowSubView.subviews)
{
for (UIView *popoverSubview in dimmingViewSubviews.subviews)
{
if([NSStringFromClass([popoverSubview class]) isEqualToString:@"UIView"])
{
for (UIView *subviewA in popoverSubview.subviews)
{
if ([NSStringFromClass([subviewA class]) isEqualToString:@"UILayoutContainerView"])
{
subviewA.layer.cornerRadius = 0;
}
for (UIView *subviewB in subviewA.subviews)
{
if ([NSStringFromClass([subviewB class]) isEqualToString:@"UIImageView"] )
{
[subviewB removeFromSuperview];
}
}
}
}
}
}
}
}
}
@end
当我想显示我的popover时,我只需调用presentPopoverWithoutInnerShadowFromRect:
和presentPopoverWithoutInnerShadowFromBarButtonItem:
方法而不是标准方法。
注意:请记住#import <QuartzCore/QuartzCore.h>
以使代码生效
答案 2 :(得分:10)
我为我正在进行的项目创建了自己的版本。
基本上你应该使用自己的自定义backgroundClass
进行弹出,在这个类中你应该定义:
- (void)willMoveToWindow:(UIWindow *)newWindow {
[super willMoveToWindow:newWindow];
if ([UIPopoverBackgroundView respondsToSelector:@selector(wantsDefaultContentAppearance)])
return;
if (![[self class] wantsDefaultContentAppearance]) {
for (UIView *view in self.superview.subviews) {
for (UIView *subview in view.subviews) {
if (subview.layer.cornerRadius != 0.0) {
subview.layer.cornerRadius = 0.0;
for (UIView *subsubview in subview.subviews) {
if (subsubview.class == [UIImageView class])
subsubview.hidden = YES;
}
}
}
}
}
}
对于iOS更新来说应该是非常防弹的。
答案 3 :(得分:3)
虽然我在校长方面同意处理此问题的正确方法是滚动自己的Popover,但在这种情况下,对于较新版本的操作系统来说,这不是问题。我是否真的只想构建和维护自己的popover实现,以支持最终无关的操作系统?如果您真的想要,请考虑网上的一些免费开源实现。
就个人而言,我调查了这里建议的方法,并以此页面为出发点(谢谢!)。它适用于两种情况(有或没有导航栏),在我看来更安全。
我没有向UIPopoverController添加方法,而是向我的UIPopoverBackgroundView添加了一个例程,以使用RELATIVE路由而不是ABSOLUTE来查找有问题的视图。简而言之,由于代码直接引用了UIPopoverBackgroundView(self),因此它可以向上导航(superview)然后向下导航(subviews)。
在两种情况下,视图树都是这样的:
使用UINavigationBar:
没有UINavigationBar:
我们感兴趣的两个视图是每个图中粗体和下划线的UILayoutView和UIImage视图。我们可以使用下面的代码(假设为ARC)从UIPopoverBackgroundView开始引用这些引用。我在我的UIPopoverBackgroundView实现中从layoutSubviews
执行此操作。
// Helper method for traversing child views based solely on class types
UIView* (^__unsafe_unretained __block traverseSubviews)(UIView*, NSArray*) = ^(UIView *root, NSArray* nodeTypes) {
NSString *typeName = [nodeTypes objectAtIndex:0];
for (UIView *subView in root.subviews) {
if ([NSStringFromClass([subView class]) isEqualToString: typeName]) {
if (nodeTypes.count == 1)
return subView;
else
return traverseSubviews(subView, [nodeTypes subarrayWithRange:NSMakeRange(1, nodeTypes.count - 1)]);
}
}
return (UIView*)nil;
};
// Find the subviews of interest, taking into account there could be a navigation bar
UIView *layoutView = traverseSubviews([self superview], @[@"UIView", @"UILayoutContainerView"]);
if (traverseSubviews(layoutView, @[@"UINavigationBar"])) {
layoutView = traverseSubviews(layoutView, @[@"UILayoutContainerView"]);
}
UIView *imageView = traverseSubviews(layoutView, @[@"UIImageView"]);
// Remove the default content appearance
layoutView.layer.cornerRadius = 0;
[imageView removeFromSuperview];
我在这里使用一个块来遍历子视图以保持代码简洁。它将视图作为起点和类名数组。类名数组是我期望的视图类序列,其中索引0是索引1的父类,索引1是索引2的父类,等等。它返回数组中最后一项表示的视图。
答案 4 :(得分:1)
我不相信使用Apple的标准UIPopover可以实现优雅/支持的方式。但是,您可以非常轻松地创建自己的自定义popover类。这里有很多关于如何在SO和更广泛的网络上的教程(甚至一些现成的下载解决方案)这样做的例子。只需将“自定义uipopover”放入Google即可...
答案 5 :(得分:0)
FrankZp
这适用于没有嵌入的视图的popOvers NavigationControllers。一旦ViewController(它是 嵌入在NavigationController中)用于popover 阴影又回来了。这有什么解决方案吗?
以下是UINavigationController的修改:
- (void)removeInnerShadow
{
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
for (UIView *windowSubView in window.subviews) {
if ([NSStringFromClass([windowSubView class]) isEqualToString:@"UIDimmingView"] {
for (UIView *dimmingViewSubviews in windowSubView.subviews) {
for (UIView *popoverSubview in dimmingViewSubviews.subviews) {
if([NSStringFromClass([popoverSubview class]) isEqualToString:@"UIView"]) {
for (UIView *subviewA in popoverSubview.subviews) {
if ([NSStringFromClass([subviewA class]) isEqualToString:@"UILayoutContainerView"]) {
subviewA.layer.cornerRadius = 0;
}
for (UIView *subviewB in subviewA.subviews) {
if ([NSStringFromClass([subviewB class]) isEqualToString:@"UILayoutContainerView"]) {
for (UIView * subviewC in subviewB.subviews) {
if ([NSStringFromClass([subviewC class]) isEqualToString:@"UIImageView"] ) {
[subviewC removeFromSuperview];
}
}
}
if ([NSStringFromClass([subviewB class]) isEqualToString:@"UIImageView"] ) {
[subviewB removeFromSuperview];
}
}
}
}
}
}
}
}
}